SpringBoot 整合 MultipartFile 轻松实现文件上传与下载
01、背景介绍
文件上传与下载是 Web 系统中最常见的应用功能,比如用户头像的上传、Excel 文件的导入和导出等。
今天通过这篇文章,我们一起来学习一下如何在 Spring Boot 中实现文件的上传与下载功能。
02、方案实践
在此,我们以Thymeleaf
页面模板引擎为例,简单介绍几种常用的文件上传和下载方式。
2.1、添加相关依赖包
首先创建一个基础的 Spring Boot 项目,并引入相关的依赖包。
<!--spring boot核心-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--springmvc web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--thymeleaf模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.2、添加相关配置参数
默认情况下,Spring Boot 已经帮我们封装好了文件上传相关的配置信息,例如上传的文件最大大小,上传文件的临时目录等。
在application.properties
配置文件中也可对相关属性进行自定义配置,内容如下:
# 表示是否开启文件上传支持,默认为 true
spring.servlet.multipart.enabled=true
# 表示上传文件的临时目录
spring.servlet.multipart.location=
# 表示文件写入磁盘的阀值,默认为 0,一般情况下不用特意修改
spring.servlet.multipart.file-size-threshold=0
# 表示上传的单个文件的最大大小,默认为 1MB
spring.servlet.multipart.max-file-size=10MB
# 表示多文件上传时文件的总大小,默认为 10MB
spring.servlet.multipart.max-request-size=10MB
# 表示文件是否延迟解析,默认为 false
spring.servlet.multipart.resolve-lazily=false
通常,spring.http.multipart.max-file-size
和spring.servlet.multipart.max-request-size
参数配置比较多,当上传的文件超过设定值,会抛出异常。
2.3、单文件上传示例
环境搭建完成之后,在src/main/resources/templates
目录下,创建一个简单的单文件上传页面upload.html
,内容如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>文件上传demo</title>
</head>
<body>
<h1>单文件上传页面</h1>
<form method="post" action="/fileUpload" enctype="multipart/form-data">
文件:<input type="file" name="file"><br>
前缀路径:<input type="text" name="prefixName"><br>
<hr>
<input type="submit" value="提交">
</form>
</body>
</html>
对应的Controller
类,示例如下:
@Controller
public class FileController {
private static final String SRC_PATH = "/Users/demo/file/";
/**
* 访问 upload1 路径时,跳转到upload.html页面
* @return
*/
@GetMapping("/upload1")
public String index() {
return "upload";
}
/**
* 单文件加表单上传
* @param file
* @param prefixName
* @return
* @throws IOException
*/
@PostMapping("/fileUpload")
@ResponseBody
public String fileUpload(@RequestParam("file") MultipartFile file,
@RequestParam("prefixName") String prefixName) throws IOException {
// 获取上传文件的文件名
String fileName = file.getOriginalFilename();
String absolutePath = SRC_PATH + prefixName + "_" + fileName;
// 将文件保存到磁盘
file.transferTo(new File(absolutePath));
return "Upload file success:" + prefixName + "_" + fileName;
}
}
启动服务后,访问http://localhost:8080/upload1
,可以看到如下界面:
选择文件并填写相关参数,点击“提交”,在服务器指定存储上传文件的目录下,可以看到上传的文件信息。
2.4、多文件上传示例
如果存在多文件多参数的上传场景,那么应该如何处理呢?
操作也很简单,将服务端的接受文件参数改成数组即可,相关示例如下!
与上面类似,创建一个多文件上传页面uploadMulti.html
,内容如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>文件上传demo</title>
</head>
<body>
<h1>多文件上传页面</h1>
<form method="post" action="/multiFileUpload" enctype="multipart/form-data">
文件1:<input type="file" name="files"><br>
文件2:<input type="file" name="files"><br>
文件前缀路径:<input type="text" name="prefixName"><br>
<hr>
<input type="submit" value="提交">
</form>
</body>
</html>
对应的Controller
类,内容如下:
@Controller
public class FilesController {
private static final String SRC_PATH = "/Users/demo/file/";
/**
* 访问 upload2 路径时,跳转到uploadMulti.html页面
* @return
*/
@GetMapping("/upload2")
public String index() {
return "uploadMulti";
}
/**
* 多文件加表单上传
* @param files
* @param prefixName
* @return
* @throws IOException
*/
@PostMapping("/multiFileUpload")
@ResponseBody
public String multiFileUpload(@RequestParam("files") MultipartFile[] files,
@RequestParam("prefixName") String prefixName) throws IOException {
for (MultipartFile file : files) {
String fileName = file.getOriginalFilename();
String absolutePath = SRC_PATH + prefixName + "_" + fileName;
file.transferTo(new File(absolutePath));
}
return "Upload file success";
}
}
启动服务后,访问http://localhost:8080/upload2
,可以看到如下界面:
选择相关文件并填写相关参数,点击“提交”,在服务器指定的目录下,可以看到与上面类似的结果。
2.5、文件下载示例
文件下载功能,应用场景也特别多,通常以 restful 方式访问服务端并获取资源,通用实现示例如下:
@Controller
public class DownloadController {
private static final String SRC_PATH = "/Users/demo/file/";
/**
* 通过文件名获取文件并以流的形式返回给客户端
* @param filename
* @param response
*/
@GetMapping("/download/{filename:.+}")
public void download(@PathVariable String filename, HttpServletResponse response) throws Exception {
File file = new File(SRC_PATH +'/'+ filename);
if(!file.exists()){
throw new RuntimeException("下载文件不存在");
}
response.reset();
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
response.setContentLength((int) file.length());
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
// 使用缓存流,边读边写
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
OutputStream os = response.getOutputStream();
byte[] buff = new byte[1024];
int i;
while ((i = bis.read(buff)) != -1) {
os.write(buff, 0, i);
os.flush();
}
} catch (IOException e) {
throw new RuntimeException("下载文件失败");
}
}
}
启动服务后,在浏览器中访问上传的文件名,例如地址http://localhost:8080/download/test_Jietu20240523-153004.jpg
,文件将以流的形式下载到本地。
其中URLEncoder.encode(filename, "UTF-8")
用意在于,防止下载中文文件名乱码。
2.6、相关异常处理
问题一:当上传的文件尺寸超过设置的最大文件大小,服务就会抛出异常,如何来捕捉这类异常呢?
可以增加一个全局异常处理类来统一处理,示例如下:
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理Exception异常
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Map exceptionHandler(Exception ex){
Map<String,Object> errorMap = new HashMap<>();
errorMap.put("code","500");
errorMap.put("message",ex.getMessage());
return errorMap;
}
}
问题二:当上传的文件大小超过 10M,浏览器出现连接已重置,无任何报错?
没错,当上传的文件大小超过 10M,确实会出现如下界面。
出现这个问题,主要在于 tomcat 的默认连接器maxSwallowSize
配置上。如果未指定,默认为 2 MB,当上传的数据超过这个值,客户端可能会被重置连接。
如果想要快速解决,可以将maxSwallowSize
设置为-1,表示无限制。只需在application.properties
文件中添加如下配置。
server.tomcat.max-swallow-size=-1
但是这样配置如果发布到生产会非常不利,比如用户尝试上传 100 MB 文件,Tomcat 将会需要更多资源来处理额外的带宽,并发量会明显下降。
03、小结
本文主要围绕在 Spring Boot 中实现文件的上传与下载功能进行相关示例介绍,如果有描述不对的地方,欢迎留言指出。
写到最后
最后感谢各位的阅读,原创不易,如果觉得文章写的不错,欢迎大家转发,点击【在看】让更多的人看到,谢谢大家的支持!