在中文環境下導出 CSV/Excel 文件時,若無 BOM(Byte Order Mark)頭,Excel 會默認按系統編碼(如 GBK)解讀,導致 UTF-8 內容顯示亂碼。本文詳細說明 BOM 原理與 PHP 添加方案。
BOM(Byte Order Mark)是文件開頭的 3 個隱藏字節:EF BB BF(十六進制),對應 UTF-8 編碼標記。Windows 記事本另存為 UTF-8 時會自動添加。系統識別到 BOM 即判定文件為 UTF-8 編碼。
<?php
// 文件名
$filename = "file.csv";
// 設置下載 Header
header('Expires: ' . gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + 10) . ' GMT');
header('Cache-Control: max-age=10');
header('Content-Type: text/csv; charset=utf-8');
header("Content-Disposition: attachment; filename={$filename}");
// 關鍵:輸出內容前先發送 BOM 頭
$out = "\xEF\xBB\xBF"; // UTF-8 BOM
// 若有提示信息放第一行
if (!empty($extra['notice'])) {
$out .= "{$extra['notice']}\r\n";
}
// 輸出數據行(示例:$table 為二維數組)
foreach ($table as $row) {
$out .= implode(",", $row) . "\r\n";
}
// 直接輸出
echo $out;
?>
<?php
$file = '/path/to/export.csv';
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . urlencode(basename($file)));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
// 發送 BOM + 文件內容
echo "\xEF\xBB\xBF";
readfile($file);
exit;
?>
"\xEF\xBB\xBF" 或 chr(239).chr(187).chr(191)text/csv; charset=utf-8 比 application/vnd.ms-excel 更標準urlencode() 編碼,否則 IE/Edge 下載亂碼readfile() 或分塊輸出| 場景 | 是否需要 BOM | 說明 |
|---|---|---|
| Excel 打開 CSV | ✅ 必須 | Excel 默認不識別無 BOM 的 UTF-8 |
| 文本編輯器打開 | ❌ 可選 | 現代編輯器自動檢測編碼 |
| 程序解析導入 | ❌ 建議無 | 多數解析庫不處理 BOM,反而干擾 |
| 數據庫 LOAD DATA | ❌ 禁止 | 會導入 BOM 字符造成髒數據 |
掌握 BOM 添加技巧,能徹底解決「導出文件在 Excel 打開亂碼」這一經典痛點。