目录
[一、MultipartFile 是什么?](#一、MultipartFile 是什么?)
[1. getOriginalFilename()](#1. getOriginalFilename())
[2. getInputStream()](#2. getInputStream())
[3. transferTo](#3. transferTo)
[三、Spring 是如何处理文件上传的?](#三、Spring 是如何处理文件上传的?)
[1. 前端发送 multipart 请求](#1. 前端发送 multipart 请求)
[2. Spring 解析请求](#2. Spring 解析请求)
[3. 转换为 MultipartFile](#3. 转换为 MultipartFile)
[四、最小可运行 Demo](#四、最小可运行 Demo)
[1. Controller 示例](#1. Controller 示例)
[2. curl 测试](#2. curl 测试)
[3. 配置文件](#3. 配置文件)
[1 错误写法](#1 错误写法)
[2 正确写法](#2 正确写法)
[3 示例](#3 示例)
[1. 文件名安全问题(路径穿越)](#1. 文件名安全问题(路径穿越))
[2. 文件类型伪造](#2. 文件类型伪造)
[3. 临时文件问题](#3. 临时文件问题)
[4. 大文件限制](#4. 大文件限制)
在日常开发中,文件传输几乎是一个"必备能力":头像上传、Excel导入、日志收集......而在 Spring Boot 体系中,处理文件上传的核心接口就是 MultipartFile。
一、MultipartFile 是什么?
MultipartFile 是 Spring 提供的一个接口,用于表示 HTTP 请求中上传的文件。当浏览器使用 multipart/form-data 提交表单时,例如:
POST /upload
Content-Type: multipart/form-data
Spring 会自动将请求解析,并把文件部分封装为 MultipartFile 对象。
可以理解为:
MultipartFile是"上传文件在服务端的抽象表示"
二、核心方法详解
MultipartFile 提供了一组非常实用的方法:
public interface MultipartFile {
String getName();
String getOriginalFilename();
String getContentType();
boolean isEmpty();
long getSize();
byte[] getBytes() throws IOException;
InputStream getInputStream() throws IOException;
void transferTo(File dest) throws IOException, IllegalStateException;
default Resource getResource() {
return new MultipartFileResource(this);
}
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
}
}
1. getOriginalFilename()
String filename = file.getOriginalFilename();
-
获取上传时的文件名
-
注意:不安全,可能被伪造
2. getInputStream()
InputStream in = file.getInputStream();
适用于:
-
上传到 HDFS / OSS / S3
-
流式处理(避免大文件内存问题)
3. transferTo
file.transferTo(new File("/data/upload/test.txt"));
特点:
-
内部已优化(可能使用零拷贝)
-
性能优于手动写流
-
使用最简单
一句话:保存文件优先用它
三、Spring 是如何处理文件上传的?
很多人只会用,不知道背后发生了什么。整个流程如下:
1. 前端发送 multipart 请求
<form method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
</form>
2. Spring 解析请求
核心组件:
MultipartResolver
默认实现:
StandardServletMultipartResolver
3. 转换为 MultipartFile
Spring 会:
-
解析 HTTP Body
-
提取文件部分
-
封装为
MultipartFile -
注入 Controller 参数
四、最小可运行 Demo
1. Controller 示例
@RestController
@RequestMapping("/file")
public class FileUploadController {
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "文件为空";
}
try {
String filename = file.getOriginalFilename();
File dest = new File("D:/upload/" + filename);
file.transferTo(dest);
return "上传成功:" + filename;
} catch (Exception e) {
e.printStackTrace();
return "上传失败";
}
}
}
2. curl 测试
curl -X POST "http://localhost:8080/file/upload" \
-H "Content-Type: multipart/form-data" \
-F "file=@D:/test/a.txt"
3. 配置文件
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
五、大文件处理与流式上传
1 错误写法
byte[] data = file.getBytes();
问题:
-
文件越大,占用内存越高
-
容易 OOM
2 正确写法
try (InputStream in = file.getInputStream()) {
// 流式处理
}
3 示例
public void uploadToHdfs(MultipartFile file) throws Exception {
FileSystem fs = FileSystem.get(new Configuration());
Path path = new Path("/data/" + file.getOriginalFilename());
try (FSDataOutputStream out = fs.create(path);
InputStream in = file.getInputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
}
六、常见坑总结
1. 文件名安全问题(路径穿越)
错误写法:
new File("/upload/" + file.getOriginalFilename());
攻击方式:
../../../../etc/passwd
正确写法:
String filename = Paths.get(file.getOriginalFilename()).getFileName().toString();
2. 文件类型伪造
file.getContentType()
不可靠!
建议:
-
校验文件头(magic number)
-
或白名单限制
3. 临时文件问题
Spring 机制:
-
小文件 → 内存
-
大文件 → 临时磁盘
默认路径:
/tmp 或 Tomcat temp
4. 大文件限制
如果没配置,会报错:
MaxUploadSizeExceededException