<!-- Maven 坐標 -->
<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("/*") // 匹配所有路徑
@MultipartConfig // Servlet 3.0 原生支援 (可選,本例用 FileUpload)
public class FileServerServlet extends HttpServlet {
// 路徑常量
private static final String UPLOAD_DIR = "upload";
private static final String TEMP_DIR = "temp";
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
private static final long MAX_REQUEST_SIZE = 100 * 1024 * 1024; // 100MB
// Action 路由
private static final String ACTION_LIST = "/list";
private static final String ACTION_UPLOAD = "/upload";
private static final String ACTION_DOWNLOAD = "/download";
private static final String ACTION_DELETE = "/delete";
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String action = req.getRequestURI().substring(req.getContextPath().length());
try {
switch (action) {
case ACTION_LIST: listFiles(req, resp); break;
case ACTION_UPLOAD: uploadFile(req, resp); break;
case ACTION_DOWNLOAD: downloadFile(req, resp); break;
case ACTION_DELETE: deleteFile(req, resp); break;
default: show404(resp);
}
} catch (Exception e) {
log("處理請求異常: " + action, e);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
// ========== 文件上傳 ==========
private void uploadFile(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 檢查是否 multipart
if (!ServletFileUpload.isMultipartContent(req)) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "非上傳請求");
return;
}
// 2. 配置工廠與解析器
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024 * 100); // 100KB 閾值,超過寫臨時文件
factory.setRepository(getTempDir(req));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding(StandardCharsets.UTF_8.name());
upload.setFileSizeMax(MAX_FILE_SIZE);
upload.setSizeMax(MAX_REQUEST_SIZE);
upload.setProgressListener((bytesRead, contentLength, items) ->
log("上傳進度: " + bytesRead + "/" + contentLength));
// 3. 解析請求
List<FileItem> items;
try {
items = upload.parseRequest(req);
} catch (FileUploadException e) {
throw new ServletException("解析失敗", e);
}
// 4. 處理每個表單項
String uploadDir = getUploadDir(req);
for (FileItem item : items) {
if (item.isFormField()) {
// 普通字段處理
String value = item.getString(StandardCharsets.UTF_8);
log("表單字段: " + item.getFieldName() + "=" + value);
} else {
// 文件處理
String fileName = sanitizeFileName(item.getName());
if (fileName == null) continue;
// 生成唯一文件名防覆蓋
String savedName = UUID.randomUUID() + "_" + fileName;
// Hash 分散存儲避免單目錄文件過多
String savePath = getHashPath(uploadDir, savedName);
try (InputStream in = item.getInputStream();
OutputStream out = new FileOutputStream(savePath)) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
log("文件上傳成功: " + savePath);
}
}
// 重定向到列表頁
resp.sendRedirect(req.getContextPath() + ACTION_LIST);
}
// ========== 文件列表 ==========
private void listFiles(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String uploadDir = getUploadDir(req);
File root = new File(uploadDir);
List<FileInfo> files = new ArrayList<>();
// 遞歸掃描 (實際項目建議用數據庫記錄元數據)
scanFiles(root, files);
// 生成 HTML 響應
resp.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = resp.getWriter()) {
out.println("文件列表 ");
out.println("");
out.println("");
out.println("文件管理
");
out.println("");
out.println("| # | 文件名 | 大小 | 操作 |
|---|---|---|---|
| " + idx++ + " | "); out.println("" + fi.originalName + " | "); out.println("" + formatSize(fi.size) + " | "); out.println("下載 | " + "刪除 | "); out.println("
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>文件上傳</title></head><body>
<h2>上傳文件</h2>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" multiple><br><br>
<input type="submit" value="上傳">
</form>
<p><a href="list">返回列表</a></p>
</body></html>
https://host:port/app/list 查看文件列表基於 Servlet + Commons FileUpload 實現的文件服務器,代碼量少、依賴輕、部署簡單,極適合內網臨時文件共享、中小項目附件管理、原型驗證場景。掌握其核心流程(解析、存儲、分散、下載、刪除),為後續遷移到專業對象存儲奠定堅實基礎。