1. 概述
本文将介绍如何在 Servlet 中实现文件上传功能。我们主要介绍两种主流方式:
✅ 原生 Jakarta EE 提供的 @MultipartConfig
注解(推荐用于 Servlet 3.0+)
✅ Apache Commons FileUpload 库(适用于老版本 Servlet API)
如果你的项目基于较新的 Jakarta EE(或旧称 Java EE),优先使用第一种方式,简单、干净、无需额外依赖。只有在维护老系统时才考虑使用 Commons FileUpload。
2. 使用 Jakarta EE 的 @MultipartConfig
从 Servlet 3.0 开始,原生支持多部分请求(multipart request),无需引入第三方库。这是目前最简单粗暴的实现方式。
2.1 HTML 表单
首先,前端表单必须设置 enctype="multipart/form-data"
,否则文件无法正确传输:
<form method="post" action="multiPartServlet" enctype="multipart/form-data">
选择文件: <input type="file" name="file" />
<input type="submit" value="上传" />
</form>
⚠️ 注意:name="file"
是后端获取 Part 时的关键标识。
2.2 配置 MultipartConfig
在你的 HttpServlet
上添加 @MultipartConfig
注解,用于设置上传限制和临时存储策略:
@MultipartConfig(
fileSizeThreshold = 1024 * 1024, // 1MB,超过则写入磁盘
maxFileSize = 1024 * 1024 * 5, // 单文件最大 5MB
maxRequestSize = 1024 * 1024 * 5 * 5 // 整个请求最大 25MB(支持多文件)
)
public class MultipartServlet extends HttpServlet {
private static final String UPLOAD_DIRECTORY = "uploads";
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) uploadDir.mkdir();
for (Part part : request.getParts()) {
String fileName = getFileName(part);
if (fileName != null && !fileName.isEmpty()) {
part.write(uploadPath + File.separator + fileName);
}
}
response.getWriter().println("上传成功!");
}
private String getFileName(Part part) {
for (String content : part.getHeader("content-disposition").split(";")) {
if (content.trim().startsWith("filename")) {
return content.substring(content.indexOf("=") + 2, content.length() - 1);
}
}
return null;
}
}
2.3 更简洁的获取文件名方式(Servlet 3.1+)
如果你使用的是 Servlet 3.1 或更高版本,可以直接调用 getSubmittedFileName()
,无需手动解析 header:
String fileName = part.getSubmittedFileName();
✅ 推荐:这种方式更安全,避免了手动字符串处理可能带来的 bug。
3. 使用 Apache Commons FileUpload
如果你还在维护一个古老的 Servlet 2.5 项目,无法使用 @MultipartConfig
,那么 Apache Commons FileUpload 是经典选择。
3.1 依赖配置
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
⚠️ 注意:commons-io
是 FileUpload
的运行时依赖,漏掉会抛 NoClassDefFoundError
。
3.2 实现上传 Servlet
Commons FileUpload 的核心流程如下:
- 判断请求是否为 multipart
- 配置
DiskFileItemFactory
(内存/磁盘缓存策略) - 使用
ServletFileUpload
解析请求 - 遍历
FileItem
并保存文件
public class FileUploadServlet extends HttpServlet {
private static final int MEMORY_THRESHOLD = 1024 * 1024; // 1MB
private static final long MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB
private static final long MAX_REQUEST_SIZE = 1024 * 1024 * 25; // 25MB
private static final String UPLOAD_DIRECTORY = "uploads";
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (!ServletFileUpload.isMultipartContent(request)) {
response.getWriter().println("请求不是 multipart 类型");
return;
}
// 配置工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(MEMORY_THRESHOLD);
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
// 创建上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(MAX_FILE_SIZE);
upload.setSizeMax(MAX_REQUEST_SIZE);
// 创建上传目录
String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) uploadDir.mkdir();
try {
List<FileItem> formItems = upload.parseRequest(request);
if (formItems != null && !formItems.isEmpty()) {
for (FileItem item : formItems) {
if (!item.isFormField()) { // 只处理文件字段
String fileName = new File(item.getName()).getName();
String filePath = uploadPath + File.separator + fileName;
File storeFile = new File(filePath);
item.write(storeFile);
request.setAttribute("message", "文件 " + fileName + " 上传成功!");
}
}
}
} catch (Exception ex) {
request.setAttribute("message", "上传失败: " + ex.getMessage());
}
response.getWriter().println(request.getAttribute("message"));
}
}
✅ 踩坑提示:
item.getName()
返回的是全路径(如C:\Users\test\file.txt
),所以要用new File().getName()
提取文件名item.write()
会自动处理临时文件的移动,无需手动操作
4. 运行示例
将项目打包为 .war
文件,部署到 Tomcat(如 webapps/ROOT.war
),启动后访问:
http://localhost:8080/
你会看到上传页面:
选择文件并上传后,显示成功提示:
最后,检查服务器上的 uploads
目录,确认文件已保存:
5. 总结
方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
@MultipartConfig |
Servlet 3.0+ | 原生支持,零依赖,代码简洁 | 老项目不兼容 |
Commons FileUpload | Servlet < 3.0 | 兼容性好,功能稳定 | 需引入额外依赖 |
✅ 建议:新项目一律使用 @MultipartConfig
,避免引入不必要的第三方库。
❌ 避免混用两种方式,容易导致请求解析冲突。
完整示例代码已托管至 GitHub:https://github.com/eugenp/tutorials/tree/master/web-modules/javax-servlets