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

解決 Firefox/IE 下載中文文件名亂碼:通用編碼工具類實現

     編輯:Java 相關知識

問題背景

Web 應用提供下載功能時,若文件名包含中文,不同瀏覽器對 Content-Disposition 標頭中 filename 參數的編碼處理不一致,導致下載對話框顯示亂碼。典型差異:

  • IE / Edge (舊版):要求 filename 為 UTF-8 URL 編碼(URLEncoder.encode)。
  • Firefox / Chrome / Safari / 新版 Edge:支持 RFC 5987 格式(filename*=UTF-8''encoded),也兼容 ISO-8859-1 編碼的 filename。

本文提供一個零依賴、可直接集成的工具類,根據 User-Agent 自動選擇編碼策略,兼容主流瀏覽器。

核心工具類實現

package com.example.util;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * 下載文件名編碼工具類
 * 

根據 User-Agent 自動選擇編碼方式,解決中文文件名在不同瀏覽器下的亂碼問題。

*/ public class DownloadFilenameEncoder { /** * 編碼下載文件名 * * @param request HttpServletRequest,用於獲取 User-Agent * @param fileName 原始文件名(含中文) * @return 編碼後的文件名,可直接用於 Content-Disposition 的 filename 參數 */ public static String encode(HttpServletRequest request, String fileName) { if (fileName == null) return ""; String userAgent = request.getHeader("User-Agent"); try { // IE / 老版 Edge (Trident 引擎) if (userAgent != null && userAgent.contains("MSIE") || userAgent.contains("Trident")) { // URLEncoder.encode 會將空格編碼為 +,需替換為 %20 符合 RFC return URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); } // 現代瀏覽器:Firefox, Chrome, Safari, 新版 Edge (Chromium) // 採用 ISO-8859-1 "偽編碼",瀏覽器會按 UTF-8 解碼 return new String(fileName.getBytes("UTF-8"), "ISO-8859-1"); } catch (UnsupportedEncodingException e) { // 理論不會發生,JDK 必備編碼 return fileName; } } /** * 生成完整的 Content-Disposition 標頭值(推薦使用) *

同時輸出 filename (兼容舊瀏覽器) 和 filename* (RFC 5987 標準), * 瀏覽器會優先使用支持的那一個。

* * @param request HttpServletRequest * @param fileName 原始文件名 * @return 完整標頭值,如:attachment; filename="fallback.txt"; filename*=UTF-8''real%20name.txt */ public static String buildContentDisposition(HttpServletRequest request, String fileName) { String encodedLegacy = encode(request, fileName); String encodedRfc5987; try { encodedRfc5987 = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); } catch (UnsupportedEncodingException e) { encodedRfc5987 = encodedLegacy; } return "attachment; filename=\"" + encodedLegacy + "\"; filename*=UTF-8''" + encodedRfc5987; } }

Controller 中使用示例

@GetMapping("/download")
public void downloadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String originalName = "產品手冊_2024版.pdf";
    String contentDisposition = DownloadFilenameEncoder.buildContentDisposition(request, originalName);

    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", contentDisposition);
    // response.setContentLengthLong(file.length()); // 建議設置

    try (InputStream in = new FileInputStream("/data/files/manual.pdf");
         OutputStream out = response.getOutputStream()) {
        byte[] buffer = new byte[8192];
        int len;
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        out.flush();
    }
}

編碼策略對照表

瀏覽器類型User-Agent 關鍵字編碼方式標頭示例
IE 6-11MSIE / TridentUTF-8 URL Encodefilename="%E4%B8%AD%E6%96%87.txt"
FirefoxFirefoxISO-8859-1 偽編碼 / RFC 5987filename="中文.txt"; filename*=UTF-8''%E4%B8%AD%E6%96%87.txt
Chrome / 新版 EdgeChrome / EdgISO-8859-1 偽編碼 / RFC 5987同 Firefox
SafariSafariISO-8859-1 偽編碼 / RFC 5987同 Firefox

進階說明

  1. 雙標頭策略:buildContentDisposition() 同時輸出 filename 和 filename*,現代瀏覽器優先使用 RFC 5987 格式的 filename*,舊瀏覽器回退到 filename,實現最廣兼容。
  2. 空格處理:URLEncoder.encode 將空格編碼為 +,但 RFC 3986 規定路徑中空格應為 %20,故需 .replaceAll("\\+", "%20")。
  3. 文件名安全:若文件名來自用戶輸入,務必過濾 \、/、:、*、?、"、<、>、| 等非法字符,防止路徑遍歷或標頭注入。
  4. Spring Boot 響應式:WebFlux 中同樣適用,只需在 ServerHttpResponse 設置標頭。

常見坑點排查

  • 亂碼依然存在:確認響應流未被其他過濾器/攔截器二次包裝導致標頭丟失;檢查文件名是否已在 Controller 層被錯誤編碼過兩次。
  • 文件名截斷:舊版 IE 對 filename 長度有 255 字節限制,超長文件名需截斷或僅保留關鍵部分。
  • 代理/殺毒軟件干擾:部分企業代理會剝離/修改 Content-Disposition,需在網絡層面排查。

總結

通過 User-Agent 判斷瀏覽器類型,採用「舊瀏覽器 URL 編碼 + 現代瀏覽器 ISO-8859-1 偽編碼」雙策略,並建議同時輸出 RFC 5987 標準的 filename* 參數,可徹底解決中文下載文件名在主流瀏覽器中的亂碼問題。工具類無第三方依賴,複製即用,適合所有 Servlet 容器環境。

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