理论介绍
在Java Web的开发之中,我们不可避免会有文件的上传与下载的需求,文件的上传与下载,在Spring MVC的时代文件的上传与下载比较配置比较复杂,或者说在Spring MVC的时代开发的配置都比较复杂,随着Spring Boot的普及,现在我们我们的开发已经比较简单,对于上传与下载,需要注意的就是MultipartFile
类型与HttpServletResponse
请求,这两个是比较重要的地方。
文件上传的时候,使用MultipartFile
来包装上传的文件的信息,MultipartFile
是一个接口,我们在Http请求之中使用的具体的子类是StandardMultipartFile
,其中封装了文件名称,文件类型,文件大小,内容,然后提供了一个文件复制的方法,如下:
文件下载,主要就是在客户端发送一个下载的请求,然后把文件从浏览器之中下载下来,这个过程之中,我们要找到请求的文件的位置,然后把它作为输入,输出到我们的输出流之中,此处就要使用到我们上述的HttpServletResponse
响应对象,把请求到的文件的内容输出到响应的输出流之中即可。当然需要在输出之前设置响应的类型,header信息,关键的代码如下:
1response.setContentType("application/octet-stream");
2response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
代码实战
事先配置
我们要上传文件,就要开启上传,然后对于请求的大小和上传的文件的大小要有一个设置,同时,上传时还必须给一个存储的路径,这样我们就可以把上传的文件,存储到我们指定的位置了,具体的配置如下:
1# 开启上传和下载
2spring.servlet.multipart.enabled=true
3# 最大的文件大小
4spring.servlet.multipart.max-file-size=20MB
5# 单次最大请求大小
6spring.servlet.multipart.max-request-size=20MB
7
8# 自定义的上传文件存放路径
9file.upload.dir=d:/test
需要说明的是,开启上传默认是true,如果不想开启这个功能,我们可以设置false关闭上传功能,自定义的上传文件路径,这个字段是自定义的,没有标准的字段,也可以不配置,直接在代码里面设置,但是这样会显得混乱,不利于我们开发。
1@RestController
2@RequestMapping("file")
3@Slf4j
4public class FileController {
5 @Value("${file.upload.dir}")
6 private String uploadFilePath;
7
8 @PostMapping("/uploadFile")
9 public String fileUpload(@RequestParam("file") MultipartFile file) throws JSONException {
10 JSONObject result = new JSONObject();
11 if (file.isEmpty()) {
12 result.put("error", "空文件!");
13 return result.toString();
14 }
15 // 文件名
16 String fileName = file.getOriginalFilename();
17 String suffixName = fileName.substring(fileName.lastIndexOf("."));
18 log.info("上传文件名称为:{}, 后缀名为:{}!", fileName, suffixName);
19
20 File fileTempObj = new File(uploadFilePath + "/" + fileName);
21 // 检测目录是否存在
22 if (!fileTempObj.getParentFile().exists()) {
23 fileTempObj.getParentFile().mkdirs();
24 }
25 // 使用文件名称检测文件是否已经存在
26 if (fileTempObj.exists()) {
27 result.put("error", "文件已经存在!");
28 return result.toString();
29 }
30
31 try {
32 // 写入文件:方式1
33 // file.transferTo(fileTempObj);
34 // 写入文件:方式2
35 FileUtil.writeBytes(file.getBytes(), fileTempObj);
36 } catch (Exception e) {
37 log.error("发生错误: {}", e);
38 result.put("error", e.getMessage());
39 return result.toString();
40 }
41
42 result.put("success", "文件上传成功!");
43 return result.toString();
44 }
45
46 /**
47 * 多个文件上传
48 *
49 * @param files
50 * @return
51 * @throws JSONException
52 */
53 @ResponseBody
54 @PostMapping("/uploadFiles")
55 public String fileUploads(@RequestParam("files") MultipartFile files[]) throws JSONException {
56 JSONObject result = new JSONObject();
57
58 for (int i = 0; i < files.length; i++) {
59 String fileName = files[i].getOriginalFilename();
60 File dest = new File(uploadFilePath + '/' + fileName);
61 if (!dest.getParentFile().exists()) {
62 dest.getParentFile().mkdirs();
63 }
64 try {
65 files[i].transferTo(dest);
66 } catch (Exception e) {
67 log.error("发生错误: {}", e);
68 result.put("error", e.getMessage());
69 return result.toString();
70 }
71 }
72 result.put("success", "文件上传成功!");
73 return result.toString();
74 }
75
76
77 // 下载到了默认的位置
78 @ResponseBody
79 @GetMapping("/downloadFile")
80 public String fileDownload(HttpServletResponse response, @RequestParam("fileName") String fileName) throws JSONException, IOException {
81 JSONObject result = new JSONObject();
82
83 File file = new File(uploadFilePath + '/' + fileName);
84 if (!file.exists()) {
85 result.put("error", "下载文件不存在!");
86 return result.toString();
87 }
88
89 response.reset();
90 response.setContentType("application/octet-stream");
91 response.setCharacterEncoding("utf-8");
92 response.setContentLength((int) file.length());
93 response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
94
95 // 原生的方式
96 // try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));) {
97 // byte[] buff = new byte[1024];
98 // OutputStream os = response.getOutputStream();
99 // int i = 0;
100 // while ((i = bis.read(buff)) != -1) {
101 // os.write(buff, 0, i);
102 // os.flush();
103 // }
104 // } catch (IOException e) {
105 // log.error("发生错误: {}", e);
106 // result.put("error", e.getMessage());
107 // return result.toString();
108 // }
109 // 简单方式: 方式1
110 // byte[] bytes = FileCopyUtils.copyToByteArray(file);
111 // 简单方式: 方式2
112 byte[] readBytes = FileUtil.readBytes(file);
113 OutputStream os = response.getOutputStream();
114 os.write(readBytes);
115 result.put("success", "下载成功!");
116 return result.toString();
117 }
118
119}
在上面的上传文件之中,写入文件方式1和写入文件方式2的作用是相同的,前者是使用的StandardMultipartFile
的transferTo方法,把该文件,传送到我们指定的位置;第二种方法,是先取到这个文件的内容,单位是Byte,然后再去写入,有个限制就是这个文件的大小不能大于Integer.MAX_VALUE
,也就是2GB,这个对于普通的需求完全够用了,如果想一次性上传这么大的文件,然后一次性获取内容字节,然后再去写入,那这个时候我们就需要使用大文件上传的方式了,必须要先对文件分片,然后一片一片写入对应片的内容即可,这个在后续会讲解,此处的第二种方式,主要是要个给大家介绍一个工具,就是Hutool,里面有各种好用的Util工具,可以帮助我们快速开发,我们就不需要自己写各种Util了,别人的轮子直接使用,除非没有满足我们需求的,我们自己实现,这样可以加快我们开发的效率。
下载文件的代码之中也提供了不同的方式,前者是Spring自带的工具,后者是Hutool,作用是相同的。
单独的接口的测试,可以使用postman,如果不想使用这种方式,可以添加前端的页面,我的项目里面完成了前后端的交互,页面效果如下,前端UI是Bootstrap,多个文件的上传使用了Bootstrap Fileinput控件,项目仓库:上传与下载项目地址。
本篇文章来源于微信公众号: 疾风小虎牙
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13930.html