<form action="fileServlet?method=upload" method="post" enctype="multipart/form-data">
用戶名:<input type="text" name="userName"><br/>
文件:<input type="file" name="file_img"><br/>
<input type="submit" value="提交">
</form>
method="post":大數據必須 POSTenctype="multipart/form-data":二進制流編碼,非默認 application/x-www-form-urlencoded<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>
@WebServlet("/fileServlet")
public class FileServlet extends HttpServlet {
private static final String UPLOAD_DIR = "upload";
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getParameter("method");
if ("upload".equals(method)) upload(req, resp);
else if ("downList".equals(method)) downList(req, resp);
else if ("down".equals(method)) download(req, resp);
}
private void upload(HttpServletRequest req, HttpServletResponse resp) {
try {
// 1. 工廠 & 核心對象
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(10 * 1024 * 1024); // 10MB 單文件
upload.setSizeMax(50 * 1024 * 1024); // 50MB 總大小
upload.setHeaderEncoding("UTF-8"); // 解決中文文件名亂碼
// 2. 解析請求
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
if (item.isFormField()) { // 普通字段
String val = item.getString("UTF-8");
System.out.println(item.getFieldName() + "=" + val);
} else { // 文件字段
String fileName = item.getName();
// 防重名:UUID + 原文件名
String savedName = UUID.randomUUID() + "#" + fileName;
String savePath = getServletContext().getRealPath("/") + UPLOAD_DIR;
File dir = new File(savePath);
if (!dir.exists()) dir.mkdirs();
File dest = new File(dir, savedName);
item.write(dest); // 寫入磁盤
item.delete(); // 刪除臨時文件
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void downList(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String basePath = getServletContext().getRealPath("/") + UPLOAD_DIR;
File dir = new File(basePath);
String[] names = dir.list();
Map<String, String> map = new LinkedHashMap<>(); // 保留順序
if (names != null) {
for (String full : names) {
String shortName = full.substring(full.lastIndexOf("#") + 1);
map.put(full, shortName);
}
}
req.setAttribute("fileNames", map);
req.getRequestDispatcher("/downlist.jsp").forward(req, resp);
}
private void download(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String fileName = req.getParameter("fileName");
fileName = new String(fileName.getBytes("ISO-8859-1"), "UTF-8"); // GET 參數解碼
String basePath = getServletContext().getRealPath("/") + UPLOAD_DIR;
File file = new File(basePath, fileName);
if (!file.exists()) {
resp.sendError(404);
return;
}
// 設置下載頭
String encodedName = URLEncoder.encode(fileName.substring(fileName.lastIndexOf("#") + 1), "UTF-8");
resp.setHeader("Content-Disposition", "attachment; filename=" + encodedName);
resp.setContentType("application/octet-stream");
try (InputStream in = new FileInputStream(file);
OutputStream out = resp.getOutputStream()) {
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
}
}
}
@MultipartConfig(
location = "/tmp", // 臨時文件目錄
fileSizeThreshold = 1024*1024, // 1MB 以上寫磁盤
maxFileSize = 10*1024*1024, // 10MB
maxRequestSize = 50*1024*1024 // 50MB
)
@WebServlet("/fileServlet3")
public class FileServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 獲取 Part
Part filePart = req.getPart("file_img");
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
String savedName = UUID.randomUUID() + "#" + fileName;
String savePath = getServletContext().getRealPath("/upload") + File.separator + savedName;
filePart.write(savePath); // 自動處理臨時文件移動
filePart.delete();
// 普通字段
String userName = req.getParameter("userName");
}
}
| 難點 | 解決方案 |
|---|---|
| 中文文件名亂碼 | FileUpload: setHeaderEncoding("UTF-8");Servlet 3.0: 容器需配置 URIEncoding="UTF-8" |
| 文件名衝突 | UUID + 原文件名,或 MD5 + 擴展名 |
| 大文件內存溢出 | FileUpload: DiskFileItemFactory 閾值;Servlet 3.0: fileSizeThreshold |
| 下載中文文件名 | URLEncoder.encode(name, "UTF-8") + Content-Disposition: attachment; filename=xxx |
| 斷點續傳/秒傳 | 前端分片,後端 RandomAccessFile.seek() 追寫,維護分片索引 |
| 安全校驗 | 限制副檔名、MIME 類型、掃毒、存儲非 Web 目錄 |
<c:forEach items="${fileNames}" var="entry">
<a href="fileServlet?method=down&fileName=${entry.key}">${entry.value}</a><br/>
</c:forEach>
Commons FileUpload 兼容性好、控制力強,適合舊項目維護;Servlet 3.0 @MultipartConfig 零依賴、代碼極簡,是新項目首選。無論哪種方案,文件名去重、大小限制、編碼處理、安全校驗、臨時文件清理 是生產級上傳下載的五大基石。