完整示例:Thymeleaf + Spring Boot 文件上传(单文件/多文件)
一、项目结构
src/main/
├── java/
│ └── com.example.demo/
│ ├── controller/FileController.java
│ └── DemoApplication.java
├── resources/
│ └── templates/
│ └── upload.html
│ └── application.properties
└── static/
└── upload/
二、关键代码实现
1. 表单页面(upload.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>文件上传</title>
</head>
<body>
<!-- 单文件上传(HttpServletRequest) -->
<form th:action="@{/upload/request}" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">上传(HttpServletRequest)</button>
</form>
<!-- 单文件上传(MultipartFile) -->
<form th:action="@{/upload/multipart}" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">上传(MultipartFile)</button>
</form>
<!-- 单文件上传(Part) -->
<form th:action="@{/upload/part}" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">上传(Part)</button>
</form>
<!-- 多文件上传(MultipartFile) -->
<form th:action="@{/upload/multi/multipart}" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple />
<button type="submit">上传多文件(MultipartFile)</button>
</form>
</body>
</html>
2. 控制器(FileController.java)
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class FileController {
// 打开文件上传页面
@GetMapping("/upload/page")
public String uploadPage() {
return "upload"; // 对应 templates/upload.html
}
// === 使用 HttpServletRequest ===
@PostMapping("/upload/request")
@ResponseBody
public Map<String, Object> uploadRequest(HttpServletRequest request) {
// 强制转换为 MultipartHttpServletRequest(需Spring支持)
if (request instanceof MultipartHttpServletRequest mreq) {
MultipartFile file = mreq.getFile("file");
if (file != null && !file.isEmpty()) {
try {
file.transferTo(new File(file.getOriginalFilename()));
return dealResultMap(true, "上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
return dealResultMap(false, "上传失败");
}
// === 使用 MultipartFile(单文件)===
@PostMapping("/upload/multipart")
@ResponseBody
public Map<String, Object> uploadMultipartFile(MultipartFile file) {
if (!file.isEmpty()) {
try {
file.transferTo(new File(file.getOriginalFilename()));
return dealResultMap(true, "上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
return dealResultMap(false, "上传失败");
}
// === 使用 Part(单文件)===
@PostMapping("/upload/part")
@ResponseBody
public Map<String, Object> uploadPart(Part filePart) {
if (filePart != null && filePart.getSize() > 0) {
try {
filePart.write(filePart.getSubmittedFileName());
return dealResultMap(true, "上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
return dealResultMap(false, "上传失败");
}
// === 多文件上传(MultipartFile)===
@PostMapping("/upload/multi/multipart")
@ResponseBody
public Map<String, Object> uploadMultiFiles(@RequestParam("files") MultipartFile[] files) {
boolean allSuccess = true;
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
file.transferTo(new File(file.getOriginalFilename()));
} catch (IOException e) {
e.printStackTrace();
allSuccess = false;
}
}
}
return dealResultMap(allSuccess, allSuccess ? "所有文件上传成功" : "部分或全部文件上传失败");
}
// === 辅助方法:返回结果 ===
private Map<String, Object> dealResultMap(boolean success, String msg) {
Map<String, Object> result = new HashMap<>();
result.put("success", success);
result.put("msg", msg);
return result;
}
}
3. 配置文件(application.properties)
# 启用文件上传支持
spring.servlet.multipart.enabled=true
# 单文件最大大小(示例:10MB)
spring.servlet.multipart.max-file-size=10MB
# 总请求最大大小(示例:20MB)
spring.servlet.multipart.max-request-size=20MB
# 临时文件存储路径(可选)
spring.servlet.multipart.location=src/main/static/upload/
三、不同参数对比表格
参数类型 | 功能描述 | 依赖性 | 易用性 | 适用场景 |
---|---|---|---|---|
HttpServletRequest | 需强制转换为MultipartHttpServletRequest ,通过getFile() 操作文件 | Spring框架 + Servlet API | 中(需类型转换) | 兼容旧代码或特殊需求 |
MultipartFile | Spring封装的文件对象,提供丰富的文件操作方法(如getOriginalFilename() ) | Spring框架 | 高(直接绑定参数) | 推荐使用,开发效率高 |
Part | Servlet原生接口,直接操作上传的“部分”数据 | Servlet 3.0+ | 中(需手动处理流) | 需严格遵循Servlet标准时 |
四、关键代码说明
-
表单配置:
- 必须设置
enctype="multipart/form-data"
:否则Spring无法解析文件。 - 单文件:使用
<input type="file" name="file" />
。 - 多文件:添加
multiple
属性并使用MultipartFile[]
或List<Part>
接收。
- 必须设置
-
控制器方法:
- HttpServletRequest:需强制转换为
MultipartHttpServletRequest
,通过getFile()
获取文件。 - MultipartFile:Spring自动绑定文件,直接通过
@RequestParam
接收。 - Part:需通过
@RequestParam
或Part
参数直接接收,手动处理流。
- HttpServletRequest:需强制转换为
-
文件保存:
- 使用
transferTo()
(MultipartFile
)或write()
(Part
)将文件保存到指定路径。
- 使用
五、核心差异总结
维度 | HttpServletRequest | MultipartFile | Part |
---|---|---|---|
依赖框架 | Spring + Servlet API | Spring | Servlet 3.0+ |
绑定方式 | 需强制转换为MultipartHttpServletRequest | 直接通过@RequestParam 绑定 | 直接通过@RequestParam 绑定 |
文件操作 | 需手动处理类型转换 | 封装方法丰富(如获取原始文件名) | 需手动处理流和元数据 |
推荐场景 | 兼容旧代码或特殊需求 | 新项目开发(简洁易用) | 需严格遵循Servlet标准时 |
六、注意事项
- 文件大小限制:需在
application.properties
中调整max-file-size
和max-request-size
。 - 临时目录:默认使用系统临时目录,可通过
spring.servlet.multipart.location
自定义。 - 异常处理:需添加
try-catch
处理文件读写异常(示例中简化处理)。