Web 開發中文件上傳是高頻需求。Servlet 3.0+ 雖原生支持 @MultipartConfig,但 Apache Commons FileUpload 仍是兼容性最好、功能最豐富的選擇。本文從依賴引入、前端表單、Servlet 核心代碼、配置參數、進階功能(大小限制、編碼、臨時目錄、進度監聽)全方位講解。
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username" placeholder="用戶名" />
<input type="file" name="file1" />
<input type="file" name="file2" multiple />
<button type="submit">上傳</button>
</form>
multiple 屬性允許單個 file 控件選擇多文件@WebServlet("/upload")
@MultipartConfig // Servlet 3.0+ 可選,FileUpload 不依賴此註解
public class UploadServlet extends HttpServlet {
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
private static final long MAX_REQUEST_SIZE = 50 * 1024 * 1024; // 50 MB
private static final String UPLOAD_DIR = "upload";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
PrintWriter out = resp.getWriter();
// 1. 創建工廠:設置內存閾值與臨時目錄
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(4 * 1024); // 4 KB 以上寫入臨時文件
factory.setRepository(new File(getServletContext().getRealPath("/tmp")));
// 2. 創建解析器
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(MAX_FILE_SIZE);
upload.setSizeMax(MAX_REQUEST_SIZE);
upload.setHeaderEncoding("UTF-8"); // 解決中文文件名亂碼
// 3. 解析請求
try {
List items = upload.parseRequest(req);
for (FileItem item : items) {
if (item.isFormField()) {
// 普通表單字段
out.println(item.getFieldName() + " = " + item.getString("UTF-8"));
} else {
// 文件字段
String fileName = Paths.get(item.getName()).getFileName().toString(); // 防止路徑遍歷
if (fileName.isEmpty()) continue;
// 生成唯一文件名:UUID + 原文件名
String savedName = UUID.randomUUID() + "#" + fileName;
File saveDir = new File(getServletContext().getRealPath(UPLOAD_DIR));
if (!saveDir.exists()) saveDir.mkdirs();
File savedFile = new File(saveDir, savedName);
item.write(savedFile); // 寫入最終位置
item.delete(); // 刪除臨時文件
out.println("文件上傳成功: " + fileName + " → " + savedName);
out.println("大小: " + item.getSize() + " bytes");
}
}
} catch (FileUploadException e) {
out.println("解析失敗: " + e.getMessage());
} catch (Exception e) {
out.println("處理異常: " + e.getMessage());
e.printStackTrace();
}
}
}
<servlet>
<servlet-name>upload</servlet-name>
<servlet-class>com.example.UploadServlet</servlet-class>
<init-param>
<param-name>filePath</param-name>
<param-value>upload</param-value>
</init-param>
<init-param>
<param-name>tempFilePath</param-name>
<param-value>tmp</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>upload</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
item.getContentType()、擴展名、魔數(文件頭字節)setFileSizeMax(單文件)、setSizeMax(總請求),超限拋 FileSizeLimitExceededException/SizeLimitExceededExceptionProgressListener 接口,前端 Ajax 輪詢進度item.getInputStream() 直接流式寫入,避免 item.write() 內存拷貝Paths.get(name).getFileName()savedFile.setExecutable(false)CommonsMultipartResolver,Controller 直接用 MultipartFile 參數| 現象 | 原因 | 解決 |
|---|---|---|
| item.isFormField() 全為 true | 表單缺少 enctype | 添加 enctype="multipart/form-data" |
| 中文文件名亂碼 | 編碼不一致 | upload.setHeaderEncoding("UTF-8") |
| 上傳大文件失敗 | 超過默認限制 | 調大 setSizeMax、setFileSizeMax |
| 臨時文件未清理 | 異常路徑未 delete | finally 塊調用 item.delete() |
Commons FileUpload 以成熟穩定、配置靈活、兼容性強成為文件上傳首選。掌握 DiskFileItemFactory、ServletFileUpload 核心類,配合大小限制、編碼處理、安全防範、流式寫入最佳實踐,能構建企業級可靠上傳服務。Servlet 3.0+ 環境下可考慮原生 API 簡化依賴,但 FileUpload 仍是遷移期與老項目維護的最佳選擇。