在 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;
}
}
程式碼中特別註明使用 HttpStatus.OK (200) 而非 HttpStatus.CREATED (201),這是因為 IE 和 Edge 瀏覽器無法正確識別 201 狀態碼,會導致下載失敗或異常。Chrome 和 Firefox 則無此問題。
透過解析 HTTP 請求標頭中的 USER-AGENT 字串,針對不同瀏覽器採用不同編碼策略:
URLEncoder.encode(fileName, "UTF-8")new String(fileName.getBytes("UTF-8"), "ISO-8859-1")URLEncoder 會將空格編碼為 + 號,但在 IE/Edge 中這會導致檔名顯示異常,因此需額外將 + 替換為 %20。
StreamingResponseBody 或 ResourceHttpMessageConverter 進行流式傳輸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 專案中實用的基礎組件。