基礎上傳範例
JSP 表單
<form action="${pageContext.request.contextPath}/UploadServlet"
enctype="multipart/form-data" method="post">
上傳用戶:<input type="text" name="username" /><br/>
上傳文件1:<input type="file" name="file1" /><br/>
上傳文件2:<input type="file" name="file2" /><br/>
<input type="submit" value="上傳" />
</form>
Servlet 核心邏輯
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
if (!upload.isMultipartContent(request)) return;
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
// 普通欄位
String value = item.getString("UTF-8");
} else {
// 文件欄位
String filename = item.getName().substring(item.getName().lastIndexOf("\\") + 1);
InputStream in = item.getInputStream();
// 寫入文件...
}
}
生產級上傳增強功能
| 功能 | 實現方式 |
| 中文檔名亂碼 |
upload.setHeaderEncoding("UTF-8") |
| 安全存儲路徑 |
文件存放於 WEB-INF/upload 等外部不可直接訪問目錄 |
| 防止文件覆蓋 |
生成唯一文件名:UUID.randomUUID().toString() + "_" + 原檔名 |
| 目錄分散存儲 |
雜湊算法打散:hashCode & 0xF 兩級子目錄 |
| 限制單文件大小 |
upload.setFileSizeMax(1024 * 1024 * 10) (10MB) |
| 限制總上傳大小 |
upload.setSizeMax(1024 * 1024 * 100) (100MB) |
| 文件類型驗證 |
檢查副檔名白名單 |
| 上傳進度監聽 |
實作 ProgressListener 介面 |
| 動態增加上傳欄位 |
JavaScript createElement("input") 動態添加 |
完整上傳 Servlet 關鍵代碼
public class UploadServlet1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String savePath = getServletContext().getRealPath("/WEB-INF/upload");
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(getServletContext().getRealPath("/WEB-INF/temp")));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
upload.setFileSizeMax(1024 * 1024 * 10);
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
String value = item.getString("UTF-8");
} else {
String filename = makeFileName(item.getName());
String realPath = makePath(filename, savePath);
// 寫入文件流...
item.delete(); // 刪除臨時文件
}
}
}
private String makeFileName(String filename) {
return UUID.randomUUID().toString() + "_" + filename.substring(filename.lastIndexOf("\\") + 1);
}
private String makePath(String filename, String savePath) {
int hash = filename.hashCode();
int d1 = hash & 0xF;
int d2 = (hash & 0xF0) >> 4;
String dir = savePath + "\\" + d1 + "\\" + d2;
new File(dir).mkdirs();
return dir;
}
}
文件下載實現
列出可下載文件
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String filepath = getServletContext().getRealPath("/WEB-INF/upload");
Map<String, String> map = new HashMap<>();
listFiles(new File(filepath), map);
request.setAttribute("map", map);
request.getRequestDispatcher("/listfile.jsp").forward(request, response);
}
private void listFiles(File file, Map<String, String> map) {
if (file.isDirectory()) {
for (File f : file.listFiles()) listFiles(f, map);
} else {
String realName = file.getName().substring(file.getName().indexOf("_") + 1);
map.put(file.getName(), realName);
}
}
}
JSP 顯示下載連結
<c:forEach var="me" items="${map}">
<c:url value="/servlet/DownLoadServlet" var="downurl">
<c:param name="filename" value="${me.key}"/>
</c:url>
${me.value} <a href="${downurl}">下載</a><br/>
</c:forEach>
下載 Servlet
public class DownLoadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String filename = request.getParameter("filename");
filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
String path = makePath(filename, getServletContext().getRealPath("/WEB-INF/upload"));
File file = new File(path + "\\" + filename);
String realName = filename.substring(filename.indexOf("_") + 1);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
// 流式傳輸...
}
}
最佳實踐總結
- 上傳文件統一存放於
WEB-INF 受保護目錄
- 使用 UUID + 雜湊目錄防止衝突與效能下降
- 下載時透過
Content-Disposition: attachment 強制瀏覽器彈出下載框
- 中文檔名需經
URLEncoder.encode 處理
- 大文件採用緩衝流分塊讀寫,避免 OOM