在 Java Web 开发中,文件上传是一项非常常见的需求,例如上传头像、Excel 文件、图片、PDF、视频等。Spring MVC 为我们提供了
MultipartFile接口,使文件上传变得十分简单。本文将从原理、常用 API、实际开发、注意事项以及面试角度,全面介绍MultipartFile。
一、什么是 MultipartFile?
MultipartFile 是 Spring MVC 提供的一个接口,用于接收客户端上传的文件。
当前端通过 <input type="file"> 上传文件时,浏览器会采用 multipart/form-data 编码格式发送 HTTP 请求,SpringMVC 会自动解析请求,并封装成 MultipartFile 对象。
也就是说:
MultipartFile 本质上就是 SpringMVC 对上传文件的一层封装。
开发者无需自己解析 HTTP 请求体,只需要接收 MultipartFile 即可。
例如:
@PostMapping("/upload")
public String upload(MultipartFile file){
return "success";
}
Spring 会自动完成以下工作:
-
接收 HTTP 请求
-
解析 multipart/form-data
-
提取上传文件
-
封装成 MultipartFile
-
注入到 Controller 方法参数
整个过程开发者几乎无需额外处理。
二、文件上传流程
一次文件上传通常经历以下几个步骤:
浏览器
│
│ 选择文件
▼
multipart/form-data 请求
│
▼
DispatcherServlet
│
▼
MultipartResolver
│
▼
MultipartFile
│
▼
Controller
│
▼
保存到磁盘/数据库/OSS/MinIO
可以理解为:
浏览器负责发送文件。
SpringMVC 负责解析文件。
MultipartFile 负责保存文件信息。
开发者负责决定文件最终存放的位置。
三、Spring Boot 上传配置
Spring Boot 默认已经集成了文件上传功能,只需要配置上传大小即可。
spring:
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 20MB
参数说明:
| 配置项 | 说明 |
|---|---|
| enabled | 是否开启文件上传 |
| max-file-size | 单个文件最大大小 |
| max-request-size | 整个请求最大大小 |
例如:
一次上传:
头像.jpg
2MB
允许上传。
如果上传:
视频.mp4
50MB
则会抛出:
MaxUploadSizeExceededException
四、MultipartFile 常用方法
Spring 提供了许多常用方法。
1、获取文件名
String fileName = file.getOriginalFilename();
例如:
photo.png
2、获取文件大小
long size = file.getSize();
返回:
204800
单位:
Byte
3、获取文件类型
String type = file.getContentType();
例如:
image/png
image/jpeg
application/pdf
4、判断是否为空
file.isEmpty();
开发中通常都会先判断:
if(file.isEmpty()){
return "文件不能为空";
}
5、获取输入流
InputStream inputStream = file.getInputStream();
适用于:
-
上传 OSS
-
上传 MinIO
-
上传七牛云
-
上传腾讯 COS
因为这些云存储通常直接接收 InputStream。
6、保存文件
最常用的方法:
file.transferTo(dest);
例如:
File dest = new File("D:/upload/test.png");
file.transferTo(dest);
Spring 会自动完成文件复制。
五、实现单文件上传
Controller:
@RestController
@RequestMapping("/file")
public class FileController {
@PostMapping("/upload")
public String upload(MultipartFile file) throws IOException {
if(file.isEmpty()){
return "文件为空";
}
File dir = new File("D:/upload");
if(!dir.exists()){
dir.mkdirs();
}
File dest = new File(dir,file.getOriginalFilename());
file.transferTo(dest);
return "上传成功";
}
}
上传成功后:
D:
upload
test.jpg
六、多文件上传
HTML:
<input type="file" multiple>
Controller:
@PostMapping("/uploads")
public String uploads(MultipartFile[] files) throws IOException {
for (MultipartFile file : files) {
if(file.isEmpty()){
continue;
}
File dest = new File("D:/upload",file.getOriginalFilename());
file.transferTo(dest);
}
return "success";
}
Spring 会自动封装多个文件。
七、防止文件重名
实际开发中不能直接使用原文件名。
例如:
用户 A:
logo.png
用户 B:
logo.png
第二次上传会覆盖第一次。
因此一般都会生成 UUID。
String uuid = UUID.randomUUID().toString();
String suffix = file.getOriginalFilename()
.substring(file.getOriginalFilename().lastIndexOf("."));
String fileName = uuid + suffix;
生成:
c9a22d1d-0b7d-4a31-a11d.png
这样可以避免文件名冲突。
八、文件类型校验
不能相信用户上传的数据。
例如:
有人可能把:
病毒.exe
改成:
图片.jpg
所以需要校验。
简单方式:
String type = file.getContentType();
if(!type.startsWith("image/")){
return "只能上传图片";
}
更严格的方式:
读取文件内容判断文件真实格式。
很多企业都会使用 Apache Tika 等工具进行检测。
九、文件大小校验
除了 Spring 配置限制。
代码里通常还会再次判断。
if(file.getSize() > 5 * 1024 * 1024){
return "文件不能超过5MB";
}
双重校验更加安全。
十、上传到服务器还是对象存储?
企业开发中一般有三种方式。
第一种:
保存到服务器本地。
D:/upload
适用于:
-
学习
-
小项目
第二种:
保存到共享存储(NAS)。
多个服务器都可以访问。
第三种:
保存到对象存储。
例如:
-
阿里云 OSS
-
MinIO
-
腾讯云 COS
-
七牛云 Kodo
这是目前企业最常见的方案。
优点:
-
容量大
-
支持 CDN
-
支持权限控制
-
便于扩容
-
不占应用服务器磁盘
十一、MultipartFile 与 File 的区别
| 对比项 | MultipartFile | File |
|---|---|---|
| 来源 | Spring MVC | Java IO |
| 是否表示上传文件 | 是 | 否 |
| 是否依赖 Web 请求 | 是 | 否 |
| 是否能直接保存 | 可以 | 需要自己写流 |
| 使用场景 | 文件上传 | 本地文件操作 |
可以简单理解为:
-
MultipartFile表示用户上传过程中的文件。 -
File表示服务器磁盘上的文件。
在文件上传时,两者通常会配合使用:
File dest = new File("D:/upload/test.png");
multipartFile.transferTo(dest);
MultipartFile 负责接收上传内容,File 负责表示保存目标。
十二、常见异常
1. 上传文件为空
原因:
未选择文件或上传失败。
解决:
if(file.isEmpty()){
return "请选择文件";
}
2. 文件超过限制
异常:
MaxUploadSizeExceededException
解决:
调整 spring.servlet.multipart.max-file-size 和 max-request-size 配置。
3. 保存路径不存在
原因:
目标目录未创建。
解决:
File dir = new File("D:/upload");
if(!dir.exists()){
dir.mkdirs();
}
4. 文件名为空
原因:
部分浏览器或客户端未正确传递文件名。
解决:
对 getOriginalFilename() 的返回值进行判空,并结合 UUID 生成新的文件名。
十三、面试高频问题
1. MultipartFile 是什么?
它是 Spring MVC 提供的文件上传接口,用于封装客户端上传的文件信息,开发者可以通过它获取文件名、大小、类型以及文件内容,并将文件保存到指定位置。
2. MultipartFile 和 File 有什么区别?
MultipartFile 表示 HTTP 请求中上传的文件,依赖于 Web 环境;File 是 Java IO 中表示文件或目录路径的对象,主要用于本地文件系统操作。
3. transferTo() 的作用是什么?
transferTo() 用于将上传文件保存到指定位置,底层会完成文件内容的写入,开发者无需手动编写输入输出流复制代码。
4. 为什么不能直接使用原文件名保存?
因为不同用户可能上传同名文件,直接保存会导致文件被覆盖。实际开发中通常使用 UUID、雪花算法或时间戳生成唯一文件名,并保留原始扩展名。
5. 企业项目中文件一般存在哪里?
大型项目通常不会将文件直接保存在应用服务器本地,而是存储到对象存储服务(如 OSS、MinIO、COS 等),既方便扩容,也便于结合 CDN 提升访问速度。
总结
MultipartFile 是 Spring MVC 文件上传功能的核心接口,它屏蔽了底层 HTTP 请求解析的复杂过程,使开发者能够专注于业务逻辑。实际开发中,一般会结合 UUID 防止文件重名、校验文件类型和大小,并根据项目规模选择本地存储或对象存储方案。掌握 MultipartFile 的使用流程和底层原理,不仅能够满足日常开发需求,也是 Java 后端面试中的常见考点。