Spring MVC 文件上传原理与配置
理解文件上传原理有助于正确配置和处理文件上传请求。
multipart请求原理
HTTP multipart请求格式
Java
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="test.jpg"
Content-Type: image/jpeg
[文件二进制数据]
------WebKitFormBoundary
Content-Disposition: form-data; name="description"
文件描述文本
------WebKitFormBoundary--
multipart请求特点
- Content-Type为multipart/form-data
- boundary分隔符分割各部分数据
- 每部分包含Content-Disposition和Content-Type头
- 文件部分包含filename属性和二进制数据
StandardServletMultipartResolver
Spring MVC默认使用Servlet 3.0+的multipart支持:
YAML
@Configuration
public class MultipartConfig {
@Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
}
配置项详解
application.yml配置
Java
spring:
servlet:
multipart:
enabled: true # 启用multipart支持
max-file-size: 10MB # 单文件最大大小
max-request-size: 100MB # 总请求最大大小
file-size-threshold: 2KB # 内存阈值
location: /tmp/upload # 临时文件目录
resolve-lazily: false # 延迟解析
配置项说明
| 配置项 | 说明 | 默认值 |
|---|---|---|
| enabled | 是否启用multipart | true |
| max-file-size | 单文件最大大小 | 1MB |
| max-request-size | 请求总大小 | 10MB |
| file-size-threshold | 内存阈值,超过写入临时文件 | 0 |
| location | 临时文件存储位置 | 系统临时目录 |
| resolve-lazily | 是否延迟解析multipart | false |
MultipartFile接口
Java
public interface MultipartFile extends InputStreamSource {
String getName(); // 表单参数名
String getOriginalFilename(); // 原始文件名
String getContentType(); // 文件ContentType
boolean isEmpty(); // 是否为空
long getSize(); // 文件大小
byte[] getBytes(); // 获取文件字节
InputStream getInputStream(); // 获取输入流
void transferTo(File dest); // 保存到目标文件
}
文件上传处理流程
Java
HTTP multipart请求
↓
StandardServletMultipartResolver解析
↓
请求包装为StandardMultipartHttpServletRequest
↓
提取各部分数据封装为MultipartFile
↓
Controller通过@RequestParam接收MultipartFile
↓
调用transferTo()或getInputStream()处理文件
基本上传Controller
Java
@RestController
@RequestMapping("/api/upload")
public class UploadController {
@PostMapping("/single")
public String upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
throw new IllegalArgumentException("文件为空");
}
String fileName = file.getOriginalFilename();
String filePath = "/upload/" + UUID.randomUUID() + getExtension(fileName);
try {
file.transferTo(new File(filePath));
return "上传成功: " + fileName;
} catch (IOException e) {
throw new RuntimeException("上传失败", e);
}
}
private String getExtension(String fileName) {
return fileName != null && fileName.contains(".")
? fileName.substring(fileName.lastIndexOf("."))
: "";
}
}
Servlet 3.0注册配置
YAML
@Configuration
public class WebConfig {
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.ofMegabytes(10));
factory.setMaxRequestSize(DataSize.ofMegabytes(100));
factory.setFileSizeThreshold(DataSize.ofKilobytes(2));
factory.setLocation("/tmp/upload");
return factory.createMultipartConfig();
}
@Bean
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
DispatcherServlet dispatcherServlet,
MultipartConfigElement multipartConfig) {
ServletRegistrationBean<DispatcherServlet> registration =
new ServletRegistrationBean<>(dispatcherServlet, "/");
registration.setMultipartConfig(multipartConfig);
return registration;
}
}
CommonsMultipartResolver(旧版)
Spring 3.x时代使用Commons FileUpload:
Java
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10 * 1024 * 1024); // 10MB
resolver.setMaxInMemorySize(2 * 1024); // 2KB
resolver.setDefaultEncoding("UTF-8");
resolver.setUploadTempDir(new FileSystemResource("/tmp/upload"));
return resolver;
}
现推荐使用StandardServletMultipartResolver,无需额外依赖。
内存阈值作用
| threshold值 | 行为 |
|---|---|
| 0 | 所有文件写入临时目录 |
| 小于文件大小 | 超过threshold的写入临时目录 |
| 大于文件大小 | 文件保留在内存中 |
YAML
spring:
servlet:
multipart:
file-size-threshold: 2KB
- 文件小于2KB:保留在内存中
- 文件大于2KB:写入临时文件
临时文件清理
HTML
@Configuration
public class MultipartCleanupConfig {
@Bean
public MultipartFilter multipartFilter() {
return new MultipartFilter();
}
}
Spring自动清理临时文件,无需手动处理。
延迟解析
JavaScript
spring:
servlet:
multipart:
resolve-lazily: true
- false:请求到达立即解析multipart
- true:实际需要时才解析multipart
延迟解析可节省内存,但可能导致异常时机不一致。
文件上传表单
Java
<form action="/api/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">上传</button>
</form>
enctype必须设置为multipart/form-data。
AJAX上传
text
function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
fetch('/api/upload', {
method: 'POST',
body: formData
});
}
// 不设置Content-Type,浏览器自动设置multipart/form-data
MultipartFile常用方法
text
@PostMapping("/info")
public Map<String, Object> fileInfo(@RequestParam("file") MultipartFile file) {
Map<String, Object> info = new HashMap<>();
info.put("name", file.getName()); // 表单参数名
info.put("originalName", file.getOriginalFilename()); // 原始文件名
info.put("contentType", file.getContentType());
info.put("size", file.getSize()); // 字节数
info.put("sizeKB", file.getSize() / 1024);
info.put("sizeMB", file.getSize() / 1024 / 1024);
info.put("empty", file.isEmpty());
return info;
}
要点总结
- multipart/form-data是文件上传的标准格式
- StandardServletMultipartResolver是默认解析器
- max-file-size限制单文件大小
- max-request-size限制总请求大小
- file-size-threshold决定内存或临时文件存储
- MultipartFile接口提供文件操作方法
📝 发现内容有误?点击此处直接编辑