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

Java 文件上傳實戰:Spring MVC 手動解析 vs MultipartFile 封裝對比

     編輯:Java 相關知識

前言

文件上傳是 Web 開發高頻需求。Spring MVC 提供 MultipartFile 封裝簡化開發,但理解底層 Commons FileUpload 手動解析流程,有助於處理大文件、進度監聽、安全校驗等進階場景。本文對比兩種實現方式並給出生產級最佳實踐。

一、方式一:手動解析 —— 完全控制

1.1 依賴

<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>

1.2 核心流程

  1. DiskFileItemFactory:配置內存閾值(超過寫臨時文件)、臨時目錄。
  2. ServletFileUpload:設置編碼、單文件/總大小限制、進度監聽器。
  3. upload.parseRequest(request) → List。
  4. 遍歷 FileItem:isFormField() 區分普通字段與文件。
  5. 文件存儲:UUID + 原文件名 防衝突,hash 分目錄 防單目錄過多,流式寫入 FileOutputStream。
  6. 清理:item.delete() 刪除臨時文件。

1.3 關鍵代碼片段

DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(100 * 1024); // 100KB 閾值
factory.setRepository(tmpDir);

ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
upload.setFileSizeMax(20 * 1024 * 1024); // 20MB
upload.setSizeMax(40 * 1024 * 1024);     // 40MB

List items = upload.parseRequest(request);
for (FileItem item : items) {
    if (item.isFormField()) { /* 普通字段 */ }
    else { /* 文件字段 */
        String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1);
        String ext = filename.substring(filename.lastIndexOf(".")+1);
        if ("rar".equals(ext) || "zip".equals(ext)) throw new BizException("禁止上傳壓縮包");
        try (InputStream in = item.getInputStream();
             FileOutputStream out = new FileOutputStream(targetFile)) {
            IOUtils.copy(in, out); // 或手動 byte[] buffer 循環
        }
        item.delete();
    }
}

1.4 適用場景

  • 需自定義進度條、斷點續傳邏輯。
  • 非 Spring 環境(Servlet/JSP 老項目)。
  • 需在解析階段即攔截惡意文件(如掃毒)。

二、方式二:Spring MultipartFile —— 開箱即用

2.1 配置

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="41943040"/> <!-- 40MB -->
    <property name="maxInMemorySize" value="102400"/> <!-- 100KB -->
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

或 Spring Boot application.yml:

spring:
  servlet:
    multipart:
      max-file-size: 20MB
      max-request-size: 40MB
      file-size-threshold: 100KB

2.2 Controller 代碼

@PostMapping("/upload/resume")
public String uploadResume(@RequestParam("file") MultipartFile file,
                           @RequestParam(value = "oldUrl", required = false) String oldUrl,
                           HttpServletRequest request) {
    // 1. 刪除舊文件
    if (oldUrl != null) deleteDir(new File(uploadRoot + oldUrl));
    
    // 2. 校驗
    if (file.isEmpty()) return "4"; // 文件未獲取
    String original = StringUtils.deleteWhitespace(file.getOriginalFilename());
    String ext = original.substring(original.lastIndexOf(".")).toLowerCase();
    if (!Set.of(".doc",".docx",".xls",".xlsx",".ppt",".pptx",".pdf").contains(ext))
        return "6"; // 類型不合法
    
    // 3. 存儲:時間戳目錄 + 原文件名
    String dir = uploadRoot + File.separator + System.currentTimeMillis();
    File target = new File(dir, original);
    target.getParentFile().mkdirs();
    try { file.transferTo(target); } 
    catch (IOException e) { return "3"; }
    
    return File.separator + "files" + File.separator + ...; // 返回相對路徑
}

2.3 核心優勢

  • 代碼極簡,自動綁定請求參數。
  • 內建臨時文件管理、編碼處理、大小限制。
  • transferTo() 底層優化:若是臨時文件直接 renameTo 避免拷貝。

三、生產級增強清單(兩種方式通用)

維度措施
安全1. 白名單校驗擴展名/MIME 類型 2. 文件內容掃毒 3. 存儲目錄不可直接 Web 訪問(放 WEB-INF 或對象存儲) 4. 文件名去特殊字符、UUID 重命名
性能1. 大文件分片上傳 + 合併 2. 異步處理(消息隊列)3. 對象存儲(S3/OSS)直傳,後端只簽名
可靠性1. 斷點續傳(記錄已上傳分片)2. 上傳完成異步校驗 MD5 3. 失敗自動重試/告警
監控1. 上傳耗時/大小/成功率指標 2. 進度回調(WebSocket/SSE)

四、總結對比

維度手動解析MultipartFile
代碼量大(~80 行)小(~30 行)
靈活度極高(流程每步可控)中(受限於封裝行為)
學習成本高(需懂 FileItem/Factory)低
推薦場景進度條、掃毒、非 Spring、特殊協議常規業務、快速開發、Spring Boot

建議:常規業務首選 MultipartFile;進階需求在 MultipartFile 基礎上擴展(如繼承 MultipartResolver 自定義解析),避免重複造輪子。

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