SpringBoot的文件上传与下载
技术概述
文件的上传与下载是Web应用常用的功能。例如本博客园就拥有文件上传和附件下载的功能。博客内容也可直接上传图片并显示。SpringBoot是Web应用开发的主流框架,支持文件上传与下载操作。其中文件上传可分为单文件上传和多文件上传。
SpringBoot的文件上传下载实现大致步骤如下
- 前端编写上传控件
- 在Controller分别实现单文件上传、多文件上传与文件下载方法
- (可选)进行文件上传相关配置
具体实现步骤
1)前端上传控件编写
注意声明form的enctype属性为multipart/form-data
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h3>单文件上传</h3>
<form method="post" action="upload" enctype="multipart/form-data">
<input type="file" name="file"/>
<br/><br/>
<input type="submit" value="上传">
</form>
<br/><hr/>
<h3>多文件上传</h3>
<form method="post" action="multiUpload" enctype="multipart/form-data">
<input type="file" name="file" value="选择文件1"/>
<br/><br/>
<input type="file" name="file" value="选择文件2"/>
<br/><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
2)声明文件上传与下载路径常量
一般而言,文件保存路径和下载路径是固定的,因此最好将其声明为常量,当路径要修改时也只需修改常量即可。
private static final String UPLOAD_PATH = "C:/Temp/";
private static final String DOWNLOAD_PATH = "C:/Java/";
3)实现文件上传方法
文件上传方法的思路主要如下
- 空文件不执行上传操作。
- 给文件按照一定的命名规则进行重命名。此举即可以防止命名冲突,也可以从一定程度上防止病毒木马等入侵,此处采用UUID实现重命名,当然也可以采取其它命名策略(如SHA、MD5等),但是要保证命名的合理性和安全性。
- 若文件命名策略可能生成同名文件(如两个用户上传了同一个文件),可采用后上传的文件不执行保存操作。
@PostMapping("upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file) {
//不可以上传空文件
if(file.isEmpty()) {
return "文件为空,请选择文件后再上传";
}
String filename = file.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
filename = UPLOAD_PATH + UUID.randomUUID() + suffix;
File dest = new File(filename);
//若文件已存在则不执行保存操作
try {
if(!dest.exists()) {
file.transferTo(dest);
}
} catch (Exception e) {
e.printStackTrace();
return "上传失败";
}
return "上传成功";
}
4)实现多文件上传方法
相比单文件上传,多文件上传要先从request中获取文件列表,然后再对列表中的文件一一处理即可,处理策略同上述单文件上传方法
@PostMapping("multiUpload")
@ResponseBody String multiUpload(HttpServletRequest request) {
List<MultipartFile> list = ((MultipartHttpServletRequest)request).getFiles("file");
for(MultipartFile file : list) {
//不可以上传空文件
if(file.isEmpty()) {
return "某文件为空,请选择文件后再上传";
}
String filename = file.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
filename = UPLOAD_PATH + UUID.randomUUID() + suffix;
File dest = new File(filename);
//若文件已存在则不执行保存操作
try {
if(!dest.exists()) {
file.transferTo(dest);
}
} catch (Exception e) {
e.printStackTrace();
return "上传失败";
}
}
return "上传成功";
}
5)实现文件下载方法
要记得判断所需求的文件是否存在,并设置header
@GetMapping("download")
@ResponseBody
public String download(HttpServletResponse response, String requestFilename) {
File file = new File(DOWNLOAD_PATH + requestFilename);
if (!file.exists()) {
return "所访问的资源不存在";
}
try {
FileInputStream fis = new FileInputStream(file);
// 设置相关格式
response.setContentType("application/force-download");
// 设置下载后的文件名以及header
response.addHeader("Content-disposition", "attachment;fileName=" + file.getName());
OutputStream os = response.getOutputStream();
byte[] buffer = new byte[DOWNLOAD_BUFFER_SIZE];
int len = 0;
while((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
fis.close();
} catch (Exception e) {
e.printStackTrace();
return "下载失败";
}
return "开始下载";
}
6)(可选)文件上传相关配置
在application.properties或application.yml中可设置单个文件最大值max-file-size以及允许上传文件总大小max-requset-size,下以yml为例
spring:
servlet:
multipart:
max-file-size: 8MB
max-requset-size: 16MB
结果展示
上传三张图片,分别使用单文件和多文件上传
问题总结
- 要切记前端控件的name的值要和controller方法中参数的名称要一致。
- 要根据不同情况选择不同的命名策略,若无特殊要求,推荐UUID + 日期 + 后缀名的命名方式.
- 有时要上传静态资源文件,而且前端需要立刻使用上传后的文件,那么就需要实现SpringBoot热部署,该内容不在本篇博客的讨论范围,具体请移步SpringBoot实现热部署。