commons-fileupload 是 Java Web 開發中處理 multipart/form-data 表單上傳的經典元件。本文以完整 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>
public class Upfile {
private String id; // UUID 主鍵
private String uuidName; // 隨機檔名
private String fileName; // 原始檔名
private String savePath; // 伺服器儲存路徑
private Timestamp uploadTime;
private String description;
private String username;
// Getter/Setter/建構子 略
}
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username" placeholder="用戶名" />
<input type="file" name="file" />
<textarea name="description" placeholder="描述"></textarea>
<button type="submit">上傳</button>
</form>
關鍵: enctype="multipart/form-data" 且 method 必須為 POST。
public class WebUtil {
// 單檔 20MB、總計 50MB 限制
private static final long FILE_SIZE_MAX = 20 * 1024 * 1024;
private static final long TOTAL_SIZE_MAX = 50 * 1024 * 1024;
public static Upfile upload(HttpServletRequest request)
throws FileSizeLimitExceededException, SizeLimitExceededException {
Upfile upfile = new Upfile();
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024 * 1024); // 1MB 記憶體緩衝
factory.setRepository(new File(request.getServletContext().getRealPath("/tmp")));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
upload.setFileSizeMax(FILE_SIZE_MAX);
upload.setSizeMax(TOTAL_SIZE_MAX);
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) { // 普通表單欄位
String val = item.getString("UTF-8");
if ("username".equals(item.getFieldName())) upfile.setUsername(val);
else if ("description".equals(item.getFieldName())) upfile.setDescription(val);
} else { // 檔案欄位
String origName = item.getName().substring(item.getName().lastIndexOf("\\") + 1);
String uuidName = UUID.randomUUID().toString().replace("-", "") + "_" + origName;
String basePath = request.getServletContext().getRealPath("/WEB-INF/upload");
String savePath = generateRandomDir(basePath, uuidName); // 雜湊分目錄
try (InputStream in = item.getInputStream();
OutputStream out = new FileOutputStream(new File(savePath, uuidName))) {
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
}
upfile.setId(UUID.randomUUID().toString());
upfile.setUuidName(uuidName);
upfile.setFileName(origName);
upfile.setSavePath(savePath);
upfile.setUploadTime(new Timestamp(System.currentTimeMillis()));
item.delete(); // 清理暫存
}
}
return upfile;
}
// 雜湊分目錄,避免單目錄檔案過多
private static String generateRandomDir(String base, String uuidName) {
int hash = uuidName.hashCode();
StringBuilder sb = new StringBuilder();
while (hash > 0) {
sb.append('/').append(hash & 0xF);
hash >>= 4;
}
String path = base + sb;
new File(path).mkdirs();
return path;
}
}
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (!ServletFileUpload.isMultipartContent(req)) {
req.setAttribute("msg", "請設定 enctype=multipart/form-data");
req.getRequestDispatcher("/upload.jsp").forward(req, resp);
return;
}
try {
Upfile upfile = WebUtil.upload(req);
new UpfileServiceImpl().add(upfile); // 持久化到 DB
req.setAttribute("msg", "上傳成功");
} catch (FileSizeLimitExceededException e) {
req.setAttribute("msg", "單檔超過 20MB 限制");
} catch (SizeLimitExceededException e) {
req.setAttribute("msg", "總大小超過 50MB 限制");
} catch (Exception e) {
req.setAttribute("msg", "上傳失敗:" + e.getMessage());
e.printStackTrace();
}
req.getRequestDispatcher("/upload.jsp").forward(req, resp);
}
}
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String id = req.getParameter("id");
Upfile upfile = new UpfileServiceImpl().findById(id);
if (upfile == null) { resp.sendError(404); return; }
File file = new File(upfile.getSavePath(), upfile.getUuidName());
if (!file.exists()) { resp.sendError(404, "檔案已遺失"); return; }
resp.setContentType("application/octet-stream");
resp.setHeader("Content-Disposition",
"attachment; filename=\"" + URLEncoder.encode(upfile.getFileName(), "UTF-8") + "\"");
resp.setContentLengthLong(file.length());
try (InputStream in = new FileInputStream(file);
OutputStream out = resp.getOutputStream()) {
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
}
}
}
CREATE TABLE upfiles (
id VARCHAR(100) PRIMARY KEY,
uuid_name VARCHAR(255),
file_name VARCHAR(255),
save_path VARCHAR(500),
upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
description VARCHAR(500),
username VARCHAR(50)
);
DiskFileItemFactory 設定閾值,超過寫入磁碟暫存區,上傳完成後呼叫 item.delete() 釋放空間。upload.setHeaderEncoding("UTF-8") 解決中文檔名亂碼;下載時 URLEncoder.encode 處理回應標頭。