「產生 Excel」需求常見於報表匯出、資料備份、模板下載。方案選擇取決於:格式複雜度(單純表格 vs 樣式/圖表/公式)、資料量強>(百筆 vs 萬筆)、效能/記憶體強>限制。本文比較三大主流方案並提供選型指南。
利用瀏覽器將 Tab 分隔文字自動識別為 Excel 可開啟格式。適用於純資料表格、無樣式、中小資料量。
<?php
// 設定下載標頭
header('Content-Type: application/vnd.ms-excel; charset=utf-8');
header('Content-Disposition: attachment; filename="report_' . date('YmdHis') . '.xls"');
header('Pragma: public');
header('Cache-Control: max-age=0');
// 輸出資料:欄位用 \t 分隔,列用 \n 換行
echo "ID\tName\tEmail\tAmount\n";
echo "1\t張三\tzhang@example.com\t100\n";
echo "2\t李四\tli@example.com\t200\n";
// ... 迴圈輸出資料庫查詢結果
?>
| 優點 | 缺點 |
|---|---|
| 零依賴、極快、記憶體極低 (可邊查詢邊輸出) | 無法設定欄寬、字型、顏色、凍結窗格、公式 |
| 相容所有 Excel 版本、LibreOffice、Google Sheets | 無法多工作表、圖表、圖片 |
可直接 echo 串流輸出,支援百萬列 | 中文檔名需 rawurlencode 處理 |
<?php
header('Content-Type: application/vnd.ms-excel; charset=utf-8');
header('Content-Disposition: attachment; filename="report.xls"');
echo "\xEF\xBB\xBF"; // UTF-8 BOM,讓 Excel 自動識別編碼
echo "欄位1\t欄位2\n資料1\t資料2\n";
?>
PHPExcel 已停止維護 (2017),請改用其繼任者 PhpSpreadsheet。支援 .xlsx、.xls、.ods、.csv、PDF、HTML 等格式,完整樣式、公式、圖表、圖片、資料驗證。
composer require phpoffice/phpspreadsheet
<?php
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('銷售報表');
// 標題列
$headers = ['ID', '客戶', '金額', '日期'];
$sheet->fromArray($headers, null, 'A1');
// 樣式:粗體、背景色、置中、邊框
$style = [
'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']],
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '4472C4']],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
];
$sheet->getStyle('A1:D1')->applyFromArray($style);
// 資料列 (假設從資料庫查詢)
$data = [
[1, '張三', 1000, '2024-01-15'],
[2, '李四', 2500, '2024-01-16'],
];
$sheet->fromArray($data, null, 'A2');
// 金額欄位格式化為貨幣
$sheet->getStyle('C2:C' . (count($data)+1))
->getNumberFormat()->setFormatCode('#,##0.00');
// 自動欄寬
foreach (range('A', 'D') as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true);
}
// 輸出下載
$writer = new Xlsx($spreadsheet);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="report_' . date('YmdHis') . '.xlsx"');
$writer->save('php://output');
exit;
?>
<?php
// 使用 ChunkReadWriter 避免一次載入所有資料到記憶體
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false);
$writer->setUseDiskCaching(true); // 使用磁碟快取
$writer->save('php://output');
?>
CSV 非真正 Excel,但 Excel 可直接開啟。適合純資料、跨系統匯入、超大資料量、記憶體受限環境。
<?php
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="report_' . date('YmdHis') . '.csv"');
header('Pragma: no-cache');
// UTF-8 BOM 讓 Excel 正確識別中文
echo "\xEF\xBB\xBF";
$out = fopen('php://output', 'w');
fputcsv($out, ['ID', '客戶', '金額', '日期']); // 標題
// 分批查詢輸出 (PDO 範例)
$stmt = $pdo->query("SELECT id, name, amount, created_at FROM orders");
while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
fputcsv($out, $row);
}
fclose($out);
exit;
?>
Writer\Xls 或原生 Header| 問題 | 原因 | 解決 |
|---|---|---|
| Excel 開啟 CSV 中文亂碼 | Excel 預設用系統編碼 (Big5/GBK) 開啟 UTF-8 CSV | 加 UTF-8 BOM (\xEF\xBB\xBF) 或用「資料 → 從文字/CSV」匯入指定 UTF-8 |
| PhpSpreadsheet 記憶體耗盡 | 大資料集一次載入所有 Cell 物件 | 啟用 setUseDiskCaching(true)、分批寫入、避免過度樣式化 |
| 原生 Header 輸出中文檔名亂碼 | HTTP 標頭編碼問題 | filename*=UTF-8'' . rawurlencode($filename) (RFC 5987) |
| 數字被科學記號顯示 (1.23E+10) | Excel 自動偵測格式 | PhpSpreadsheet 設定 setFormatCode('@') 文字格式;CSV 在欄位加 =\\"12345678901\\" |
| 長數字 (身分證、手機) 截斷 | Excel 數值精度 15 位 | 設為文字格式或前加單引號 '123456789012345 |
三大方案各有甜點:原生 Header 適合快速原型與簡單匯出;PhpSpreadsheet 是功能完整的專業選擇;CSV 串流 是大資料量、低記憶體、跨平台的最佳拍檔。根據專案「格式需求 × 資料量 × 基礎設施」三維度選型,並善用上述坑點對策,即能輕鬆實作穩健的 Excel 匯出功能。