Apache Commons FileUpload是Apache基金会提供的用于处理HTTP multipart请求的开源组件,能够解析包含文件和普通表单字段的混合请求流,在Java Web开发中广泛用于实现文件上传功能。手动使用该组件解析请求流,可以跳过框架的默认封装,更灵活地控制上传过程,满足特殊的业务需求。

环境准备
首先需要在项目中引入Apache Commons FileUpload及其依赖的Apache Commons IO组件,如果使用Maven管理项目,可以在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.15.1</version>
</dependency>
核心解析步骤
1. 判断请求是否为multipart类型
在解析之前,需要先判断当前HTTP请求是否为multipart格式,避免对非上传请求执行无效解析。可以通过ServletFileUpload的isMultipartContent方法完成判断:
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.http.HttpServletRequest;
public boolean checkMultipartRequest(HttpServletRequest request) {
// 判断请求是否为multipart类型
return ServletFileUpload.isMultipartContent(request);
}
2. 创建文件上传处理器并设置限制
创建ServletFileUpload实例后,可以设置上传文件的大小限制、请求总大小限制等参数,避免恶意上传大文件占用服务器资源:
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import java.io.File;
public ServletFileUpload initFileUpload() {
// 创建磁盘文件项工厂,设置临时文件存储目录和大小阈值
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置内存中存储的最大字节数,超过则生成临时文件
factory.setSizeThreshold(1024 * 1024);
// 设置临时文件存储目录,这里使用系统临时目录
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
// 创建文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置单个文件最大大小为10MB
upload.setFileSizeMax(10 * 1024 * 1024);
// 设置整个请求的最大大小为50MB
upload.setSizeMax(50 * 1024 * 1024);
// 设置请求头的字符编码,解决普通字段中文乱码问题
upload.setHeaderEncoding("UTF-8");
return upload;
}
3. 解析请求流获取文件项
调用ServletFileUpload的parseRequest方法,即可解析请求流,得到所有表单项的集合,每个表单项对应一个FileItem实例:
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
public List<FileItem> parseRequestItems(HttpServletRequest request) throws FileUploadException {
ServletFileUpload upload = initFileUpload();
// 解析请求,获取所有表单项
return upload.parseRequest(request);
}
4. 区分处理普通字段和文件字段
遍历解析得到的FileItem集合,通过isFormField方法判断当前项是普通表单字段还是文件字段,分别进行处理:
import org.apache.commons.fileupload.FileItem;
import java.io.File;
import java.io.InputStream;
import java.util.List;
public void processFileItems(List<FileItem> items) throws Exception {
for (FileItem item : items) {
if (item.isFormField()) {
// 处理普通表单字段
String fieldName = item.getFieldName();
// 获取字段值,使用UTF-8编码避免乱码
String fieldValue = item.getString("UTF-8");
System.out.println("普通字段名:" + fieldName + ",值:" + fieldValue);
} else {
// 处理文件字段
String fileName = item.getName();
// 获取输入流读取文件内容
InputStream fileStream = item.getInputStream();
// 可以将文件保存到指定路径,这里示例保存到当前项目的upload目录下
String savePath = "upload/" + fileName;
File saveFile = new File(savePath);
// 确保父目录存在
if (!saveFile.getParentFile().exists()) {
saveFile.getParentFile().mkdirs();
}
item.write(saveFile);
System.out.println("文件保存成功,路径:" + savePath);
// 处理完成后清理临时文件
item.delete();
}
}
}
完整示例整合
以下是一个在Servlet中手动解析multipart请求流的完整示例,将上述步骤整合到一起:
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
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 FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// 判断是否为multipart请求
if (!ServletFileUpload.isMultipartContent(request)) {
response.getWriter().write("当前请求不是multipart类型");
return;
}
try {
// 初始化上传处理器
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024 * 1024);
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(10 * 1024 * 1024);
upload.setSizeMax(50 * 1024 * 1024);
upload.setHeaderEncoding("UTF-8");
// 解析请求获取表单项
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("普通字段:" + fieldName + " = " + fieldValue);
} else {
String fileName = item.getName();
if (fileName == null || fileName.trim().isEmpty()) {
continue;
}
// 简化文件名,去掉路径部分
fileName = new File(fileName).getName();
String savePath = this.getServletContext().getRealPath("/upload") + File.separator + fileName;
File saveFile = new File(savePath);
if (!saveFile.getParentFile().exists()) {
saveFile.getParentFile().mkdirs();
}
item.write(saveFile);
item.delete();
response.getWriter().write("文件上传成功,文件名:" + fileName);
}
}
} catch (FileUploadException e) {
response.getWriter().write("文件上传失败:" + e.getMessage());
} catch (Exception e) {
response.getWriter().write("处理请求失败:" + e.getMessage());
}
}
}
注意事项
- 解析请求流时,
parseRequest方法会消耗请求的输入流,因此同一个请求不能重复调用该方法,否则会解析失败。 - 处理文件字段时,
getName方法返回的完整路径在某些浏览器中会包含客户端本地路径,需要手动截取文件名部分再保存。 - 临时文件在使用完成后要及时调用
delete方法清理,避免占用服务器磁盘空间。 - 如果项目中同时使用了Spring MVC等框架,需要确保框架没有提前解析multipart请求,否则手动解析会获取不到表单项。
Apache_Commons_FileUploadmultipart请求请求流解析文件上传Servlet修改时间:2026-06-12 07:48:26