今天总结下Spring中的文件上传与下载,在写这篇博客之前,小编写过一篇有关文件上传下载的博文------ React Native 上传图片至七牛云存储 ,该博文主要聊到采用React Native 框架如何将文件上传到七牛云存储上,后端部分主要集成了七牛云提供的API,但并没有从底层讲到文件上传下载的基本原理,所以本篇博文尽量弥补之前的不足,接下来,我们一起开始吧!
就Java web 而言,对于文件上传,前端浏览器提交一个文件,然后后端处理返回文件上传成功或者失败的结果;对于文件下载,很多情况就是前端单击某个按钮或者点击某个文字链接,然后浏览器就会自动将文件上传到本地中。那么这其中的原理是什么呢?小编认为,文件的上传下载本质上是通过Java 提供的流API来处理的,在Servlet中我们可以使用Http相关的 HttpServletResponse
对象将文件流输出到浏览器,当然这涉及到我们浏览器如何识别该流是一个文件还是一个文本字符串的问题,所以文件上传下载还提到了Http请求头Mime类型的设置.
小编以前会想这么一个问题,我现在会使用html表单来上传一个文件,但当我使用一个框架的时候,我就不太明白其中的原理了。关于这个问题,小编认为原理都是一样的,最本质的,都是通过表单来提交文件上传请求,也就是html中的 <input type="file"/>
标签,无论你框架怎么封装,底层就是一个表单。下面我们讲下如何Spring boot 来实现文件上传下载,本示例前端部分采用简单的html表单,没有用到任何框架,简单易懂。
在Spring 框架中,对于文件上传下载的支持是通过 MultipartFile
接口来实现的,一个 MultipartFile
就表示客户端传过来的一个文件,而一个 MultipartFile[]
数组,则表示客户端传过来的多个文件,这在实现多文件上传非常有用,当然了,通过该接口,你可以获取客户端传过来的文件的一些基本信息,比如文件名称、文件大小等.当然你也可以让Spring限制客户端传过来文件的大小和类型(比如图片文件 png
还是文本文件 txt
),但这些功能的实现配置不在本文的范围内。
Spring MVC传统的REST风格用法是通过 @Controller
、 @ResponseBody
和 @RequestMapping
三个注解实现的,在Spring Boot 中,你只需要使用 @RestController
和 @RequestMapping
两个注解即可,简单示例如下:
/** * 测试文件上传与下载 * * @author hjw * */ @RestController public class FileUploadCotroller { @RequestMapping(value = "/upload.json", method = RequestMethod.POST) public boolean fileUpload( MultipartFile file) throws IllegalStateException, IOException { return false; } }
由于Spring框架依赖了 commons-fileupload
包,因此,我们需要在Maven的 pom.xml
文件中将该jar包引进来。
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
upload.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <form action="http://localhost:8081/upload.json" method="POST" enctype="multipart/form-data"> <input name="file" type="file"/> <button type="submit">single submit</button> </form> </body> </html>
FileUploadCotroller.java
@RestController public class FileUploadCotroller { @RequestMapping(value = "/upload.json", method = RequestMethod.POST) public boolean fileUpload( MultipartFile file) throws IllegalStateException, IOException { if (file.getSize() == 0) { return false; } System.err.println("文件是否为空 : " + file.isEmpty()); System.err.println("文件的大小为 :" + file.getSize()); System.err.println("文件的媒体类型为 : " + file.getContentType()); System.err.println("文件的名字: " + file.getName()); System.err.println("文件的originName为: " + file.getOriginalFilename()); File newFile = new File("C://Users//hjw//Desktop//" + file.getOriginalFilename()); file.transferTo(newFile); return true; } }
上面的html文件表单要设置表单的 enctype
属性为 enctype="multipart/form-data"
,并且 <input/>
标签的 name
属性值必须和后端接口的 MultipartFile
参数名一致,也就是都是 file
文件下载前端很简单,我们直接使用一个 <a><a/>
标签即可。
download.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <a href="http://localhost:8081/download.json">single download</a> </body> </html>
FileUploadCotroller.java
@RequestMapping(value="download.json") public boolean download(HttpServletResponse res) throws IOException { File file = new File("C://Users//hjw//Desktop//design//code//project-1//myapp//src//images//seats//bg1.png"); String fileName = "bg1.png"; res.setHeader("Content-Disposition", "attachment;filename=" + fileName); byte[] buff = new byte[1024]; BufferedInputStream bis = null; OutputStream os = null; try { os = res.getOutputStream(); bis = new BufferedInputStream(new FileInputStream(file)); int i = bis.read(buff); while (i != -1) { os.write(buff, 0, buff.length); os.flush(); i = bis.read(buff); } } catch (IOException e) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("success"); return false; } }
上面的文件下载采用流的形式进行传输,注意,要实现文件下载功能,我们必须在响应头中设置 Http Header
的 Content-Disposition
参数,该参数会激活浏览器的下载功能,并接受我们传输的文件。
上面提到多文件上传对应 MultipartFile[]
数组,因此我们只需改下后端的参数即可。
multiUpload.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <form action="http://localhost:8081/multiFile.json" enctype="multipart/form-data" method="post"> <input name="files" type="file" multiple="multiple" value="请选择多个文件"/> <button type="submit">multi submit second</button> </form> </body> </html>
FileUploadCotroller.java
@RequestMapping(value="multiFile.json",method=RequestMethod.POST) public boolean multiFile(MultipartFile[] files) throws IllegalStateException, IOException { if(files.length == 0) { return false; } File file = null; String path = "C://Users//hjw//Desktop//"; for (int i = 0; i < files.length; i++) { System.err.println("第" + i + "个文件的大小为" + files[i].getSize()); System.err.println("第" + i + "个文件是否为空" + files[i].isEmpty()); System.err.println("第" + i + "个文件的媒体类型为" + files[i].getContentType()); System.err.println("第" + i + "个文件的文件名为" + files[i].getName()); System.err.println("第" + i + "个文件的源文件名为" + files[i].getOriginalFilename()); file = new File(path + files[i].getOriginalFilename()); files[i].transferTo(file); } return false; }
注意我们要设置html文件 <input/>
标签的 multiple
属性为 multiple="multiple"
,该属性表示支持文件多选操作。同时,和单文件上传相同,我们的 <input/>
标签的 name
属性必须和后端的 MultipartFile[]
参数名相同,即 files
.
这些例子很简单,属于入门级别,当然文件上传下载做的好并不是这么简单的,只不过我们日常项目并不是对文件上传下载功能要求很苛刻(比如迅雷),当然,如果你想做的高级一点,你可以使用Java提供的文件随机存取类 RandomAccessFile
去实现断点上传和下载,断点上传下载的功能http也是有提供相应的支持的。 谢谢阅读!