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

Spring 通用檔案下載控制器實作指南

     編輯:Java 相關知識

Spring 檔案下載組件完整實作

在 Spring MVC 專案中,檔案下載是常見需求。本文提供一個完整、可直接使用的通用下載控制器,支援多種瀏覽器的中文檔名編碼處理,解決 IE、Edge、Chrome、Firefox 等瀏覽器的相容性問題。

核心程式碼實作

package com.hnust.common.controller;

import org.apache.commons.io.FileUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * 通用檔案下載控制器
 * 支援多瀏覽器中文檔名編碼
 */
@RestController
public class DownloadController extends BaseController {

    /**
     * 通用檔案下載方法
     * @param file 檔案物件
     * @return 檔案位元組陣列回應
     */
    public ResponseEntity export(File file) {
        return export(file.getName(), file);
    }

    /**
     * 通用檔案下載方法(指定檔名)
     * @param fileName 下載顯示的檔名
     * @param file 檔案物件
     * @return 檔案位元組陣列回應
     */
    public ResponseEntity export(String fileName, File file) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", encodeFileName(fileName));
        
        ResponseEntity response = null;
        try {
            // 使用 HttpStatus.OK (200) 而非 CREATED (201)
            // 因為 IE/Edge 無法識別 201 狀態碼
            response = new ResponseEntity<>(
                FileUtils.readFileToByteArray(file), 
                headers, 
                HttpStatus.OK
            );
        } catch (IOException e) {
            // 建議拋出自訂異常處理
            // throw new CommonException(ResponseStatusEnum.FILE_ERROR, e);
            e.printStackTrace();
        }
        return response;
    }

    /**
     * 根據瀏覽器類型編碼檔名
     * 解決中文檔名在不同瀏覽器顯示亂碼問題
     * @param fileName 原始檔名
     * @return 編碼後的檔名
     */
    private String encodeFileName(String fileName) {
        String encodedName = fileName;
        try {
            // 獲取 User-Agent 標頭判斷瀏覽器類型
            String agent = request.getHeader("USER-AGENT").toLowerCase();
            
            if (agent != null && (agent.contains("msie") || agent.contains("edge"))) {
                // IE / Edge 瀏覽器使用 UTF-8 URL 編碼
                encodedName = URLEncoder.encode(fileName, "UTF-8");
            } else if (agent.contains("safari") || agent.contains("chrome") || agent.contains("firefox")) {
                // Chrome / Firefox / Safari 使用 ISO-8859-1 編碼
                encodedName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
            } else {
                // 其他瀏覽器預設使用 UTF-8 編碼
                encodedName = URLEncoder.encode(fileName, "UTF-8");
            }
            
            // 修正 IE/Edge 中空格被編碼為 + 的問題
            encodedName = encodedName.replace("+", "%20");
            
        } catch (UnsupportedEncodingException e) {
            // 建議拋出自訂異常
            // throw new CommonException(ResponseStatusEnum.FAILURE, e);
            e.printStackTrace();
        }
        return encodedName;
    }
}

關鍵技術點解析

1. 狀態碼選擇:HttpStatus.OK vs HttpStatus.CREATED

程式碼中特別註明使用 HttpStatus.OK (200) 而非 HttpStatus.CREATED (201),這是因為 IE 和 Edge 瀏覽器無法正確識別 201 狀態碼,會導致下載失敗或異常。Chrome 和 Firefox 則無此問題。

2. 瀏覽器 User-Agent 判斷邏輯

透過解析 HTTP 請求標頭中的 USER-AGENT 字串,針對不同瀏覽器採用不同編碼策略:

  • IE / Edge:使用 URLEncoder.encode(fileName, "UTF-8")
  • Chrome / Firefox / Safari:使用 new String(fileName.getBytes("UTF-8"), "ISO-8859-1")
  • 其他瀏覽器:預設採用 UTF-8 URL 編碼

3. 空格編碼修正

URLEncoder 會將空格編碼為 + 號,但在 IE/Edge 中這會導致檔名顯示異常,因此需額外將 + 替換為 %20。

使用建議與擴展

  1. 異常處理:建議將註解掉的自訂異常拋出邏輯啟用,統一由全域異常處理器處理
  2. 大檔案支援:目前實作將整個檔案讀入記憶體,大檔案建議改用 StreamingResponseBody 或 ResourceHttpMessageConverter 進行流式傳輸
  3. 安全性考量:實際專案中應加入檔案路徑驗證,防止目錄遍歷攻擊
  4. 快取控制:可根據需求在 Header 中加入 Cache-Control、Expires 等快取相關標頭

測試驗證

部署後可透過以下方式測試:

// 控制器中注入 DownloadController
@Autowired
private DownloadController downloadController;

// 下載檔案
@GetMapping("/download")
public ResponseEntity downloadFile(HttpServletRequest request) {
    File file = new File("/path/to/your/file.pdf");
    // 將 request 傳遞給編碼方法(需修改 encodeFileName 簽名)
    return downloadController.export("中文檔名測試.pdf", file);
}

這個下載組件經過實戰驗證,能有效解決跨瀏覽器中文檔名下載問題,是 Spring MVC 專案中實用的基礎組件。

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