首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaWeb 文件上傳完整實戰:基於 Commons FileUpload 的 Servlet 實現

     編輯:Java 相關知識

技術棧與依賴

  • 核心庫:commons-fileupload-1.3.1.jar、commons-io-2.x.jar
  • 容器:Tomcat 7+ / Servlet 3.0+
  • 編碼:全鏈路 UTF-8

Maven 坐標:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

前端表單關鍵點

<form action="${pageContext.request.contextPath}/servlet/UploadServlet" 
      method="post" enctype="multipart/form-data">
    <input type="file" name="file1" />
    <input type="file" name="file2" />
    <input type="text" name="description1" />
    <input type="text" name="description2" />
    <button type="submit">上傳</button>
</form>

三大鐵律:method="post"、enctype="multipart/form-data"、name 屬性唯一且與後端對應。

web.xml Servlet 配置

<servlet>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>com.example.upload.UploadServlet</servlet-class>
    <init-param>
        <param-name>uploadPath</param-name>
        <param-value>/data/uploads/</param-value> <!-- 生產環境建議配置在外部屬性文件 -->
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/upload</url-pattern>
</servlet-mapping>

核心 Servlet 實現(精簡健壯版)

@WebServlet("/upload")
@MultipartConfig(
    maxFileSize = 10 * 1024 * 1024,      // 10MB
    maxRequestSize = 50 * 1024 * 1024,   // 50MB
    fileSizeThreshold = 1024 * 1024      // 1MB 臨界值,超過寫入臨時文件
)
public class UploadServlet extends HttpServlet {

    private static final Logger log = LoggerFactory.getLogger(UploadServlet.class);

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset=UTF-8");
        
        String uploadBase = getServletConfig().getInitParameter("uploadPath");
        File baseDir = new File(uploadBase);
        if (!baseDir.exists()) baseDir.mkdirs();

        Map<String, Object> result = new HashMap<>();
        List<Map<String, String>> uploadedFiles = new ArrayList<>();

        try {
            // Servlet 3.0+ 原生 API,無需 Commons FileUpload
            Collection<Part> parts = req.getParts();
            for (Part part : parts) {
                String fieldName = part.getName();
                
                if (part.getSize() == 0) continue; // 空字段跳過
                
                if (part.getSubmittedFileName() != null) { // 文件類型
                    String originalName = Paths.get(part.getSubmittedFileName()).getFileName().toString();
                    String storedName = UUID.randomUUID() + "_" + originalName;
                    File dest = new File(baseDir, storedName);
                    
                    part.write(dest.getAbsolutePath());
                    part.delete(); // 刪除臨時文件
                    
                    Map<String, String> fileInfo = new HashMap<>();
                    fileInfo.put("field", fieldName);
                    fileInfo.put("originalName", originalName);
                    fileInfo.put("storedName", storedName);
                    fileInfo.put("path", dest.getAbsolutePath());
                    fileInfo.put("size", String.valueOf(part.getSize()));
                    uploadedFiles.add(fileInfo);
                    
                    log.info("文件上傳成功: {} -> {}", originalName, storedName);
                } else { // 普通表單字段
                    String value = req.getParameter(fieldName);
                    result.put(fieldName, value);
                }
            }
            
            result.put("success", true);
            result.put("files", uploadedFiles);
            result.put("message", "上傳完成,共 " + uploadedFiles.size() + " 個文件");
            
        } catch (Exception e) {
            log.error("上傳異常", e);
            result.put("success", false);
            result.put("message", "上傳失敗: " + e.getMessage());
        }
        
        new ObjectMapper().writeValue(resp.getWriter(), result);
    }
}

關鍵最佳實踐

  1. 文件名去重:使用 UUID + 原始文件名,避免覆蓋與路徑遍歷攻擊。
  2. 大小限制:通過 @MultipartConfig 或 DiskFileItemFactory.setSizeThreshold 限制單文件/總請求大小。
  3. 類型校驗:結合 part.getContentType() 與文件頭魔數(Magic Number)雙重驗證,防止偽裝文件。
  4. 存儲隔離:上傳目錄置於 Web 根目錄之外,或配置 Nginx 禁止腳本執行權限。
  5. 異步上傳:大文件場景採用分片上傳 + 斷點續傳(如 Plupload、WebUploader)。

常見坑點排查

現象原因解決
request.getParameter 為 nullenctype 缺失或 Servlet 未解析 multipart檢查 form enctype、確認 @MultipartConfig 或 Commons FileUpload 配置
中文文件名亂碼Tomcat 8+ 默認 ISO-8859-1server.xml Connector 加 URIEncoding="UTF-8";或手動 new String(name.getBytes("ISO-8859-1"), "UTF-8")
大文件上傳失敗超過默認大小限制調整 maxFileSize/maxRequestSize;檢查 Nginx client_max_body_size

結語

從傳統的 Commons FileUpload 到 Servlet 3.0 原生 @MultipartConfig,文件上傳技術已趨成熟。核心在於:嚴格校驗、安全存儲、異常兜底、日誌可追溯。願此實戰指南助你快速構建生產級上傳服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved