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

Spring MVC 文件上傳下載完整配置與 Controller 實戰代碼

     編輯:Java 綜合教學

Spring MVC 文件上傳下載完整配置與 Controller 實戰代碼

文件上傳下載是 Web 開發核心功能。本文提供基於 Spring MVC 的完整 XML 配置、Controller 實現、大文件處理、中文文件名編碼解決方案及進階優化建議。

一、文件下載配置與實現

1. Spring MVC 配置(適用 100MB 以下文件)

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <!-- 位元組流轉換器,支援文件下載 -->
            <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
            <!-- 字符串轉換器,指定 UTF-8 -->
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes" value="application/json; charset=UTF-8"/>
            </bean>
        </list>
    </property>
</bean>

2. Controller 下載方法

@RequestMapping("/file/{fileName:.+}")
public ResponseEntity<byte[]> download(@PathVariable String fileName, 
                                       HttpServletRequest request, HttpServletResponse response) {
    ResponseEntity<byte[]> result = null;
    try {
        // 1. 安全處理文件名(防止路徑遍歷)
        String safeName = sanitizeFileName(fileName);
        
        // 2. 讀取文件(示例路徑,實際應配置外部存儲路徑)
        String basePath = "C:\\tempDirectory\\";
        File file = new File(basePath + safeName);
        if (!file.exists()) throw new FileNotFoundException();
        
        // 3. 讀取位元組
        byte[] content = FileUtils.readFileToByteArray(file); // Apache Commons IO
        
        // 4. 構建響應頭
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentLength(content.length);
        
        // 5. 解決中文文件名亂碼(RFC 5987 標準)
        String encodedName = URLEncoder.encode(safeName, StandardCharsets.UTF_8)
                                    .replaceAll("\\+", "%20");
        headers.setContentDispositionFormData("attachment", encodedName);
        
        result = new ResponseEntity<>(content, headers, HttpStatus.OK);
    } catch (Exception e) {
        log.error("文件下載失敗", e);
        // 可轉發到錯誤頁或返回 404
    }
    return result;
}

/** 文件名安全過濾 */
private String sanitizeFileName(String name) {
    return name.replaceAll("[\\\\/:*?\"<>|]", "_"); // 移除非法字符
}

二、文件上傳配置與實現

1. Spring MVC 配置(CommonsMultipartResolver)

<!-- 文件上傳解析器 -->
<bean id="multipartResolver" 
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"/>
    <!-- 最大上傳 1GB -->
    <property name="maxUploadSize" value="1073741824"/>
    <!-- 內存閾值 40KB,超過寫入臨時文件 -->
    <property name="maxInMemorySize" value="40960"/>
</bean>

需依賴:commons-fileupload、commons-io

2. Controller 上傳方法

@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public JsonResult upload(Doc doc, @RequestParam("uploadFile") MultipartFile file) {
    JsonResult result = new JsonResult();
    try {
        // 1. 獲取上傳目錄(建議配置在屬性文件)
        String uploadDir = servletContext.getRealPath("/upload");
        File dir = new File(uploadDir);
        if (!dir.exists()) dir.mkdirs();
        
        // 2. 原始文件名處理
        String originalName = file.getOriginalFilename();
        String safeName = originalName.replaceAll("\\s+", ""); // 去空格
        String ext = safeName.substring(safeName.lastIndexOf("."));
        
        // 3. 生成唯一文件名(防覆蓋):UUID + 原名 或 時間戳 + 原名
        String storedName = UUID.randomUUID().toString() + ext;
        File target = new File(dir, storedName);
        
        // 4. 保存文件
        file.transferTo(target); // 自動處理內存/臨時文件
        
        // 5. 業務邏輯:保存元數據到數據庫
        docService.upload(doc, storedName, originalName);
        
        result.setSuccess(true);
        result.setMsg("上傳成功");
    } catch (Exception e) {
        log.error("文件上傳失敗", e);
        result.setSuccess(false);
        result.setMsg("上傳失敗:" + e.getMessage());
    }
    return result;
}

三、大文件上傳優化(>100MB)

  • 分片上傳:前端切片(如 5MB/片),後端合併,支持斷點續傳
  • 流式處理:使用 StandardServletMultipartResolver (Servlet 3.0+) 避免全量讀入內存
  • 異步上傳:CompletableFuture 或線程池處理耗時合併/掃毒
  • 對象存儲:直傳 OSS/S3,後端僅簽名驗證,減輕應用服務器壓力

Servlet 3.0 原生上傳配置(替代 Commons)

<bean id="multipartResolver" 
      class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
web.xml 或 @MultipartConfig 配置:
<multipart-config>
    <max-file-size>1073741824</max-file-size>
    <max-request-size>2147483648</max-request-size>
    <file-size-threshold>40960</file-size-threshold>
</multipart-config>

四、常見坑點與解決

問題原因解決
中文文件名下載亂碼瀏覽器編碼不一致RFC 5987: filename*=UTF-8''encodedName
上傳大文件 OOM全量讀入 byte[]流式處理 / 分片上傳 / 增加 heap
臨時文件未清理Commons FileUpload 默認行為配置 FileCleaningTracker 或定時清理任務
Spring Boot 2.x 無需配置自動配置application.yml 配置 spring.servlet.multipart.*

五、安全最佳實踐

  1. 文件類型驗證:魔數檢測(讀取文件頭),不信任擴展名/Content-Type
  2. 文件名存儲:數據庫存 original_name、stored_name、path、size、mime_type
  3. 存儲隔離:上傳目錄不可直接 Web 訪問,通過 Controller 流式輸出
  4. 掃毒集成:上傳後異步調用 ClamAV 或云廠商掃毒 API
  5. 權限控制:下載前校驗用戶對文件的讀權限
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved