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

Spring MVC POI 導出 Excel 亂碼解決:HTTP 響應頭 Content-Disposition 編碼處理

     編輯:程式設計綜合問答

亂碼根因

瀏覽器下載檔案時,`Content-Disposition` 標頭中的 `filename` 參數**編碼處理不一致**:

  • IE/Edge 早期:需 `URLEncoder.encode(filename, "UTF-8")` (空格變 `+`)。
  • Chrome/Firefox/現代瀏覽器:支援 RFC 5987 `filename*=UTF-8''encoded` 格式。
  • Tomcat 8+ 默認 ISO-8859-1 編碼標頭值,中文直接寫入會亂碼。

通用解決方案:雙格式並存


@Controller
public class ExportController {
    
    @GetMapping("/exportExcel")
    public void exportExcel(HttpServletResponse response) throws IOException {
        String filename = "用戶數據_" + LocalDate.now() + ".xlsx";
        
        // 1. 設置 Content-Type
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        
        // 2. 處理 filename 編碼 (核心)
        String encodedFilename = encodeFilename(filename, getUserAgent(request));
        response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename);
        
        // 3. POI 生成工作簿
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("用戶列表");
        // ... 填充數據 ...
        
        // 4. 寫入輸出流
        try (ServletOutputStream out = response.getOutputStream()) {
            workbook.write(out);
        }
        workbook.close();
    }
    
    // User-Agent 判斷編碼策略
    private String encodeFilename(String filename, String userAgent) {
        try {
            String encoded = URLEncoder.encode(filename, "UTF-8").replace("+", "%20"); // 空格編為 %20
            // 簡單 UA 判斷 (生產建議用專門庫如 HttpServletRequestUtils)
            if (userAgent != null && userAgent.contains("MSIE") || userAgent.contains("Trident")) {
                return encoded; // IE 只認 filename
            }
            return encoded; // 現代瀏覽器同時支援 filename* 和 filename
        } catch (UnsupportedEncodingException e) {
            return "export.xlsx"; // 降級
        }
    }
}

Spring Boot 2.2+ 簡化寫法 (自動處理 RFC 5987)


@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
    String filename = "報表.xlsx";
    // Spring 自動處理 filename* 編碼 (需 Spring Framework 5.2+)
    response.setHeader("Content-Disposition", ContentDisposition.attachment().filename(filename, StandardCharsets.UTF_8).build().toString());
    // ... POI 寫入 ...
}

POI 單元格中文亂碼 (另一類問題)

若導出檔案打開後**單元格內容**亂碼,而非檔名:

  • 確保 Java 源文件編碼為 UTF-8 (IDE 設置)。
  • 字體支援中文:`CellStyle` 設置 `Font` 名稱為 `"Microsoft YaHei"` / `"SimSun"`。
  • 數據庫讀取編碼正確 (JDBC URL `characterEncoding=utf8`)。

大數據量導出優化 (SXSSF)


// SXSSF: 只在記憶體保留最近行,適合 10萬+ 行
Workbook workbook = new SXSSFWorkbook(100); // 保留 100 行
// 使用結束時
((SXSSFWorkbook)workbook).dispose(); // 清理臨時文件

總結檢查清單

  • [ ] `Content-Type` 正確 (`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`)。
  • [ ] `Content-Disposition` 包含 `filename*=` UTF-8 編碼格式。
  • [ ] `filename=` 同時提供兼容舊瀏覽器 (URLEncoder + 空格→%20)。
  • [ ] POI `Workbook.write(OutputStream)` 直接寫入 `response.getOutputStream()`。**勿**用 `FileOutputStream` 寫服務器磁盤再下載。
  • [ ] `Workbook.close()` 在 `finally` 或 `try-with-resources` 中釋放資源。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved