Servlet 文件上传
一、什么是 Servlet 文件上传?
Servlet 文件上传允许用户通过 Web 表单将文件(如图片、文档)上传到服务器。这在实际应用中很常见,例如用户头像上传或附件提交。HTTP 协议本身不支持直接上传文件,因此需要使用 multipart/form-data
编码的表单。Servlet 可以通过第三方库(如 Apache Commons FileUpload)或 Servlet 3.0+ 的内置 API 来处理上传。
上传过程涉及:
- 客户端:HTML 表单提交文件。
- 服务器:解析请求,保存文件到指定目录。
二、准备工作
- HTML 表单:
- 表单必须使用
method="post"
和enctype="multipart/form-data"
。 - 输入字段使用
<input type="file">
。
示例表单(upload.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="UploadServlet" method="post" enctype="multipart/form-data">
<label>选择文件:</label>
<input type="file" name="file"><br>
<input type="submit" value="上传">
</form>
</body>
</html>
- 依赖:
- 对于传统方式:添加 Apache Commons FileUpload 和 Commons IO JAR 包(下载自 Apache 官网)。
- 对于 Servlet 3.0+:无需额外依赖,但需在 web.xml 中指定 Servlet 版本为 3.0 或更高。
- 服务器目录:创建存储上传文件的目录,如
WEB-INF/uploads
,确保有写权限。
三、使用 Apache Commons FileUpload(传统方式)
适用于 Servlet 2.5 或更早版本。需要解析请求中的 multipart 数据。
代码示例(UploadServlet.java):
import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
import java.util.*;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.disk.*;
import org.apache.commons.fileupload.servlet.*;
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 检查是否为 multipart 请求
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
out.println("不是文件上传请求!");
return;
}
// 配置上传参数
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置内存阈值 - 超过后将产生临时文件并存储于临时目录中
factory.setSizeThreshold(1024 * 1024); // 1MB
// 设置临时存储目录
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置最大文件上传值
upload.setFileSizeMax(1024 * 1024 * 10); // 10MB
// 设置最大请求值 (包含文件和表单项)
upload.setSizeMax(1024 * 1024 * 50); // 50MB
// 解析请求
try {
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// 处理普通表单字段
String name = item.getFieldName();
String value = item.getString("UTF-8"); // 支持中文
out.println("表单字段: " + name + " = " + value);
} else {
// 处理文件上传
String fileName = new File(item.getName()).getName();
String filePath = getServletContext().getRealPath("/uploads") + File.separator + fileName;
File uploadedFile = new File(filePath);
item.write(uploadedFile);
out.println("文件上传成功: " + fileName);
}
}
} catch (Exception e) {
out.println("上传失败: " + e.getMessage());
}
}
}
说明:
ServletFileUpload.parseRequest(request)
:解析请求,获取FileItem
列表。item.isFormField()
:区分普通字段和文件字段。item.write(file)
:将文件写入服务器。- 支持多个文件:表单中添加多个
<input type="file">
。
四、使用 Servlet 3.0+ 内置支持
Servlet 3.0 引入 @MultipartConfig
注解和 Part
接口,简化上传过程。
代码示例(UploadServlet.java):
import javax.servlet.http.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.*;
@MultipartConfig(
fileSizeThreshold = 1024 * 1024, // 1MB
maxFileSize = 1024 * 1024 * 10, // 10MB
maxRequestSize = 1024 * 1024 * 50 // 50MB
)
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 获取文件 Part
Part filePart = request.getPart("file"); // "file" 是表单字段名
if (filePart != null) {
// 获取文件名
String fileName = getFileName(filePart);
// 保存文件
String filePath = getServletContext().getRealPath("/uploads") + File.separator + fileName;
filePart.write(filePath);
out.println("文件上传成功: " + fileName);
} else {
out.println("未选择文件!");
}
}
// 提取文件名
private String getFileName(Part part) {
String header = part.getHeader("content-disposition");
String fileName = header.substring(header.lastIndexOf("=") + 2, header.length() - 1);
return new File(fileName).getName();
}
}
说明:
@MultipartConfig
:配置上传限制,如文件大小。request.getPart("file")
:获取文件 Part 对象。part.write(path)
:直接写入文件。- 支持多个文件:使用
request.getParts()
遍历。
在 web.xml 中注册 Servlet(如果不使用注解):
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.example.UploadServlet</servlet-class>
<multipart-config>
<max-file-size>10485760</max-file-size> <!-- 10MB -->
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>
五、处理中文文件名和内容
上传文件时,中文文件名可能乱码。解决方案:
- 请求编码:
request.setCharacterEncoding("UTF-8")
。 - 提取文件名:在
getFileName()
方法中,使用new String(fileName.getBytes("ISO-8859-1"), "UTF-8")
转码(如果必要)。 - 文件内容:如果文件是文本,确保读取时指定 UTF-8。
示例修改(提取文件名支持中文):
private String getFileName(Part part) {
String header = part.getHeader("content-disposition");
String fileName = header.substring(header.lastIndexOf("=") + 2, header.length() - 1);
try {
fileName = new String(fileName.getBytes("ISO-8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new File(fileName).getName();
}
六、注意事项
- 安全性:
- 验证文件类型和大小,防止恶意上传(如可执行文件)。
- 使用 UUID 重命名文件,避免覆盖和路径注入。
- 性能:
- 大文件上传使用临时目录,避免内存溢出。
- 生产环境使用云存储(如阿里云 OSS)代替本地目录。
- 异常处理:
- 捕获
FileUploadException
或SizeLimitExceededException
处理超限情况。 - 确保目录存在,否则创建:
new File(filePath).getParentFile().mkdirs()
。
- 浏览器兼容:
- 测试不同浏览器(如 Chrome、IE),IE 可能需要额外处理文件名。
- 多个文件:
- 表单添加多个
type="file"
,服务器使用循环处理。
七、总结
Servlet 文件上传可以通过 Commons FileUpload 或 Servlet 3.0+ 的内置 API 实现,前者兼容旧版本,后者更简洁高效。重点在于正确配置表单、处理 multipart 请求,并注意中文编码和安全问题。在实际开发中,可结合 JSP 或框架如 Spring MVC 来扩展功能。
如果需要下载功能讲解或更多示例,请继续提问!