下面我将从最简单的方式(使用 Spring Boot)到更底层的 Servlet API,为你详细讲解如何在 Java 中接收 multipart/form-data。

场景分析
一个 multipart/form-data 请求通常包含两部分或更多部分:
- 普通表单字段:
username,description等,是文本数据。 - 文件字段:
avatar,document等,是二进制文件数据。
我们的目标就是高效、安全地解析这些数据。
使用 Spring Boot (推荐)
Spring Boot 对 multipart/form-data 提供了极其强大且简洁的支持,这是目前最主流、最推荐的方式。
添加依赖
确保你的 pom.xml 文件中包含了 spring-boot-starter-web 依赖,它已经包含了处理 multipart 请求所需的所有依赖。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置上传文件大小(可选但推荐)
为了避免上传大文件时出现问题,建议在 application.properties 或 application.yml 中进行配置。
application.properties
# 设置单个文件的最大大小 (e.g., 10MB) spring.servlet.multipart.max-file-size=10MB # 设置总请求的最大大小 (e.g., 10MB) spring.servlet.multipart.max-request-size=10MB
创建 Controller 接收数据
在 Controller 中,你可以使用 @RequestParam 注解来接收普通字段和文件。
示例 1:接收普通文本字段
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FormController {
@PostMapping("/api/submit-text")
public String submitText(
@RequestParam("username") String username,
@RequestParam("description") String description) {
System.out.println("Username: " + username);
System.out.println("Description: " + description);
return "Text received successfully: " + username;
}
}
使用 curl 测试:

curl -X POST http://localhost:8080/api/submit-text \ -H "Content-Type: multipart/form-data" \ -F "username=zhangsan" \ -F "description=This is a test description."
示例 2:接收单个文件
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
public class FileUploadController {
@PostMapping("/api/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "Please select a file to upload.";
}
try {
// 获取文件原始名称
String originalFilename = file.getOriginalFilename();
// 定义文件存储路径
String uploadDir = "uploads/";
// 创建上传目录(如果不存在)
File uploadPath = new File(uploadDir);
if (!uploadPath.exists()) {
uploadPath.mkdirs();
}
// 保存文件到服务器
String filePath = uploadDir + originalFilename;
file.transferTo(new File(filePath));
return "File uploaded successfully: " + filePath;
} catch (IOException e) {
e.printStackTrace();
return "Failed to upload file: " + e.getMessage();
}
}
}
使用 curl 测试:
假设你有一个名为 test.txt 的文件。
curl -X POST http://localhost:8080/api/upload \ -H "Content-Type: multipart/form-data" \ -F "file=@test.txt"
示例 3:同时接收文本字段和多个文件
这是最常见的情况,Spring 会自动将 MultipartFile[] 绑定到同名参数上。
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.List;
@RestController
public class MixedDataController {
@PostMapping("/api/submit-mixed")
public String submitMixed(
@RequestParam("title") String title,
@RequestParam("files") MultipartFile[] files) {
System.out.println("Title: " + title);
System.out.println("Number of files received: " + files.length);
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
String originalFilename = file.getOriginalFilename();
String uploadDir = "uploads/";
File uploadPath = new File(uploadDir);
if (!uploadPath.exists()) {
uploadPath.mkdirs();
}
String filePath = uploadDir + originalFilename;
file.transferTo(new File(filePath));
System.out.println("Saved file: " + filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "Mixed data received successfully!";
}
}
使用 curl 测试:
curl -X POST http://localhost:8080/api/submit-mixed \ -H "Content-Type: multipart/form-data" \ -F "title=My Project" \ -F "files=@file1.jpg" \ -F "files=@file2.png"
使用原生 Servlet API
如果你不使用 Spring Boot,或者需要更底层地控制解析过程,可以使用 Servlet API,这需要你手动处理解析逻辑。
创建 Servlet
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@MultipartConfig(
fileSizeThreshold = 1024 * 1024 * 1, // 1 MB
maxFileSize = 1024 * 1024 * 10, // 10 MB
maxRequestSize = 1024 * 1024 * 15 // 15 MB
)
public class FormDataServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取普通文本字段
String username = request.getParameter("username");
System.out.println("Username: " + username);
// 2. 获取文件部分
Part filePart = request.getPart("file");
if (filePart != null && filePart.getSize() > 0) {
String fileName = getSubmittedFileName(filePart); // 获取原始文件名
String uploadPath = getServletContext().getReal("") + File.separator + "uploads";
// 创建上传目录
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 写入文件
filePart.write(uploadPath + File.separator + fileName);
System.out.println("File uploaded to: " + uploadPath + File.separator + fileName);
}
// 3. 响应客户端
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println("Data received successfully!");
}
// Helper method to get the submitted file name
private String getSubmittedFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1);
}
}
return null;
}
}
配置 web.xml 或使用注解
使用注解 (推荐):
在 Servlet 类上添加 @WebServlet("/api/servlet-upload") 即可。
使用 web.xml:
<servlet>
<servlet-name>formDataServlet</servlet-name>
<servlet-class>com.example.FormDataServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>formDataServlet</servlet-name>
<url-pattern>/api/servlet-upload</url-pattern>
</servlet-mapping>
@MultipartConfig 注解
这个注解是关键,它告诉 Servlet 容器这个 Servlet 能够处理 multipart/form-data 请求,它包含以下重要属性:
fileSizeThreshold: 当文件大小超过这个阈值时,内容会被临时存储到磁盘上,否则存在内存中。maxFileSize: 单个文件允许的最大大小。maxRequestSize: 整个请求允许的最大大小。
使用 Apache Commons FileUpload
这是一个非常经典的第三方库,在 Spring 普及之前被广泛使用,它不依赖于任何框架,可以在任何 Java Web 应用中使用。
添加依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- 需要 commons-io 作为依赖 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
在 Servlet 中使用
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class CommonsFileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 检查是否是 multipart 请求
if (!ServletFileUpload.isMultipartContent(request)) {
// 如果不是,则按照普通请求处理或返回错误
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not a multipart request");
return;
}
// 配置上传参数
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置内存中存储数据的阈值,超过则写入临时文件
factory.setSizeThreshold(1024 * 1024 * 1); // 1 MB
// 设置临时文件存储目录
File repository = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置单个文件最大大小
upload.setFileSizeMax(1024 * 1024 * 10); // 10 MB
// 设置总请求最大大小
upload.setSizeMax(1024 * 1024 * 15); // 15 MB
try {
// 解析请求,获取所有部分
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// 处理普通表单字段
String fieldName = item.getFieldName();
String fieldValue = item.getString("UTF-8"); // 指定编码
System.out.println("Field: " + fieldName + ", Value: " + fieldValue);
} else {
// 处理文件
String fileName = new File(item.getName()).getName();
String uploadPath = getServletContext().getRealPath("") + File.separator + "uploads";
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
File storeFile = new File(uploadPath + File.separator + fileName);
// 保存文件
item.write(storeFile);
System.out.println("File uploaded to: " + storeFile.getAbsolutePath());
}
}
} catch (Exception e) {
e.printStackTrace();
}
response.setContentType("text/plain");
response.getWriter().println("Data processed by Commons FileUpload!");
}
}
总结与对比
| 特性 | Spring Boot | Servlet API (原生) | Apache Commons FileUpload |
|---|---|---|---|
| 易用性 | 极高,通过注解自动绑定 | 中等,需要手动解析 | 中等,需要手动解析 |
| 依赖 | spring-boot-starter-web |
无额外依赖 | commons-fileupload, commons-io |
| 配置 | application.properties |
@MultipartConfig 注解 |
在代码中配置 DiskFileItemFactory |
| 功能 | 强大,支持复杂对象、校验等 | 基础功能 | 基础功能,非常稳定 |
| 适用场景 | 所有 Spring Boot 项目 | 不使用框架的 Java Web 项目 | 老项目、或不想引入 Spring 的项目 |
- 如果你正在使用 Spring Boot,请毫不犹豫地选择方案一,它最简单、最安全、功能最强大。
- 如果你在一个传统的 Java Web 项目(如使用 JSP/Servlet)中,且不想引入第三方库,方案二(Servlet API)是最佳选择。
- 如果你在一个非常老的系统中,或者有特定的需求不想使用 Servlet 3.0+ 的新特性,方案三(Commons FileUpload)是一个可靠的备选方案。
