瀏覽器下載檔案時,`Content-Disposition` 標頭中的 `filename` 參數**編碼處理不一致**:
@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"; // 降級
}
}
}
@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 寫入 ...
}
若導出檔案打開後**單元格內容**亂碼,而非檔名:
// SXSSF: 只在記憶體保留最近行,適合 10萬+ 行
Workbook workbook = new SXSSFWorkbook(100); // 保留 100 行
// 使用結束時
((SXSSFWorkbook)workbook).dispose(); // 清理臨時文件