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

Java Servlet 文件下載:User-Agent 判斷檔名編碼錯誤 (contains 方法) 修正方案

     編輯:程式設計綜合問答

錯誤現象

The method contains(CharSequence) from the type String refers to the missing type CharSequence

發生在 `if (agent.contains("MSIE"))` 行。

根因:JDK 版本與 Servlet API 衝突

  • `String.contains(CharSequence)` 為 **JDK 1.5+** 引入
  • 專案可能編譯於 **JDK 1.4** 環境、或 Servlet 容器 (Tomcat 5.x) 運行於舊 JDK
  • 或 `servlet-api.jar` 版本過舊導致類別載入衝突

立即修正方案:替換為 `indexOf`


// 原始 (可能報錯)
if (agent.contains("MSIE")) { ... }

// 修正 (相容 JDK 1.4+、所有 Servlet 容器)
if (agent != null && agent.indexOf("MSIE") != -1) { ... }

`indexOf(String)` 為 **JDK 1.0 就存在**,零相容性風險。

完整修正後下載 Servlet (含最佳實務)

@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        // 1. 取得檔案絕對路徑 (實際專案請從資料庫/配置獲取,防止路徑遍歷攻擊)
        String relativePath = "/img/外國美女.jpg";
        String realPath = getServletContext().getRealPath(relativePath);
        File file = new File(realPath);
        
        if (!file.exists() || !file.isFile()) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 2. 解析檔名
        String fileName = file.getName();

        // 3. User-Agent 判斷編碼策略 (修正點)
        String agent = req.getHeader("User-Agent");
        boolean isIE = (agent != null && agent.indexOf("MSIE") != -1) 
                    || (agent != null && agent.indexOf("Trident") != -1); // IE11+
        
        try {
            if (isIE) {
                // IE/Edge (舊版) 需 URLEncoder 編碼
                fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
            } else {
                // 現代瀏覽器 (Chrome, Firefox, Edge 新版) 支援 RFC 5987
                fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), "ISO-8859-1");
            }
        } catch (UnsupportedEncodingException e) {
            // 極罕見,降級處理
            fileName = new String(fileName.getBytes(StandardCharsets.ISO_8859_1), "ISO-8859-1");
        }

        // 4. 設定回應標頭
        resp.setContentType(getServletContext().getMimeType(fileName));
        resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        resp.setContentLengthLong(file.length());

        // 5. 高效傳輸 (NIO 零拷貝 / 緩衝流)
        try (FileInputStream fis = new FileInputStream(file);
             BufferedInputStream bis = new BufferedInputStream(fis);
             ServletOutputStream sos = resp.getOutputStream()) {
            
            byte[] buffer = new byte[8192];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                sos.write(buffer, 0, len);
            }
            sos.flush();
        }
    }
}

User-Agent 編碼策略對照表 (2024 現狀)

瀏覽器User-Agent 關鍵字Content-Disposition 編碼
IE 6-10MSIE`URLEncoder.encode(UTF-8)`
IE 11Trident / rv:11`URLEncoder.encode(UTF-8)`
Edge (Legacy)Edge`URLEncoder.encode(UTF-8)`
Edge (Chromium), Chrome, Firefox, Safari-RFC 5987: `filename*=UTF-8''` + `filename=` (Latin-1 fallback)

現代標準寫法 (RFC 5987 / RFC 6266)


// 同時提供 filename (舊瀏覽器相容) 和 filename* (現代標準)
String encoded = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())
                    .replace("+", "%20") // RFC 3986: 空格編為 %20 非 +
resp.setHeader("Content-Disposition", 
    "attachment; filename=\"" + new String(fileName.getBytes(StandardCharsets.ISO_8859_1), "ISO-8859-1") + "\"; " +
    "filename*=UTF-8''" + encoded);

安全性補充:防止路徑遍歷攻擊


// 嚴格驗證 realPath 在允許目錄下
String baseDir = getServletContext().getRealPath("/protected-files");
String requestedPath = new File(baseDir, userSuppliedRelativePath).getCanonicalPath();
if (!requestedPath.startsWith(baseDir)) {
    resp.sendError(HttpServletResponse.SC_FORBIDDEN);
    return;
}

總結

`contains(CharSequence)` 編譯錯誤 → **JDK 版本過舊或 API 衝突**。立即修正:**改用 `indexOf(String) != -1`**,相容所有 JDK 1.0+ 環境。並建議升級至 **JDK 8/11/17+**、**Tomcat 9/10**、**Servlet API 4.0+**,採用 **RFC 5987 標準檔名編碼** 完美支援中文檔名下載。

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