| 方案 | 適用場景 | 優點 | 缺點 | 推薦指數 |
|---|---|---|---|---|
| CSV (fputcsv) | 純資料匯出、大數據、跨系統交換 | 極快、記憶體佔用低、通用性最強 | 無格式、無多工作表、編碼易亂碼 | ⭐⭐⭐⭐⭐ (資料導出首選) |
| HTML 表格偽裝 .xls | 簡單報表、需基本樣式、快速開發 | 代碼極簡、支援合併儲存格、CSS 樣式 | 非真實 Excel、開啟警告、大檔案效能差 | ⭐⭐⭐ (內部簡單報表) |
| XML Spreadsheet 2003 | 需格式、公式、多工作表、無依賴 | 純 PHP 生成、標準 XML、支援樣式/公式 | 檔案大、解析慢、Office 2003 格式兼容性有限 | ⭐⭐⭐ |
| PEAR Spreadsheet_Excel_Writer | 舊專案維護、需 .xls 格式 | 生成真實 BIFF .xls、支援格式 | 已停止維護、不支援 .xlsx、記憶體佔用高 | ⭐ (不建議新專案) |
| PhpSpreadsheet (PHPExcel 繼任者) | 現代專案標準選擇、完整功能需求 | 支援 .xlsx/.xls/.ods/CSV/HTML/PDF、豐富樣式/圖表/公式、活躍維護 | 大檔案記憶體壓力大、學習曲線較陡 | ⭐⭐⭐⭐⭐ (功能完整首選) |
| Box/Spout | 超大檔案(百萬行)、串流寫入/讀取 | 極低記憶體(串流)、速度快、支援 .xlsx/.ods/CSV | 功能較簡(無圖表、複雜公式、樣式受限) | ⭐⭐⭐⭐ (大數據導出首選) |
<?php
// 設置下載標頭
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="export_' . date('Ymd_His') . '.csv"');
header('Pragma: no-cache');
header('Expires: 0');
// 開啟輸出流
$output = fopen('php://output', 'w');
// 寫入 BOM(解決 Excel 打開 UTF-8 CSV 亂碼)
fwrite($output, "\xEF\xBB\xBF");
// 寫入標題行
fputcsv($output, ['ID', '姓名', 'Email', '註冊時間']);
// 模擬分批從資料庫讀取(生產器/游標)
$stmt = $pdo->query('SELECT id, name, email, created_at FROM users');
while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
fputcsv($output, $row);
}
fclose($output);
exit;
?>
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;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('用戶列表');
// 標題行樣式
$headerStyle = [
'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]]
];
$headers = ['ID', '姓名', 'Email', '註冊時間'];
$sheet->fromArray($headers, null, 'A1');
$sheet->getStyle('A1:D1')->applyFromArray($headerStyle);
// 資料寫入(批次寫入效能較好)
$data = [];
// 假設從 DB 取得 $rows
foreach ($rows as $i => $row) {
$data[] = [$row['id'], $row['name'], $row['email'], $row['created_at']];
}
$sheet->fromArray($data, null, 'A2');
// 自動欄寬
foreach (range('A', 'D') as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true);
}
// 輸出下載
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="users_' . date('Ymd_His') . '.xlsx"');
header('Cache-Control: max-age=0');
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');
exit;
?>
composer require box/spout
<?php
require 'vendor/autoload.php';
use Box\Spout\Writer\Xlsx\Writer;
use Box\Spout\Common\Entity\Row;
use Box\Spout\Common\Entity\Cell;
$writer = new Writer();
$writer->openToBrowser('huge_export_' . date('Ymd_His') . '.xlsx'); // 直接輸出到瀏覽器
// 標題行
$writer->addRow(new Row([
new Cell('ID'), new Cell('姓名'), new Cell('Email'), new Cell('註冊時間')
]));
// 分批讀取寫入(記憶體不隨行數增長)
$batchSize = 1000;
$offset = 0;
do {
$stmt = $pdo->prepare('SELECT id, name, email, created_at FROM users LIMIT ? OFFSET ?');
$stmt->execute([$batchSize, $offset]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
$writer->addRow(new Row([
new Cell($row['id']),
new Cell($row['name']),
new Cell($row['email']),
new Cell($row['created_at'])
]));
}
$offset += $batchSize;
} while (count($rows) === $batchSize);
$writer->close();
?>
<?php
// PhpSpreadsheet 讀取
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('uploaded.xlsx');
$sheet = $spreadsheet->getActiveSheet();
foreach ($sheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
$rowData = [];
foreach ($cellIterator as $cell) {
$rowData[] = $cell->getValue();
}
// 處理 $rowData...
}
// Spout 串流讀取(大檔案)
$reader = \Box\Spout\Reader\Xlsx\Reader::create();
$reader->open('huge.xlsx');
foreach ($reader->getSheetIterator() as $sheet) {
foreach ($sheet->getRowIterator() as $row) {
$cells = $row->getCells();
// $cells[0]->getValue() ...
}
}
$reader->close();
?>
$pdo->exec("SET NAMES utf8mb4")。\xEF\xBB\xBF),Excel 才能正確識別編碼。fromArray() 批次寫入,避免逐儲存格 setCellValue()。$spreadsheet->getActiveSheet()->setCalculateEngine(\PhpOffice\PhpSpreadsheet\Calculation\Calculation::instance()) 若無公式可禁用計算引擎。選型原則:純資料/大數據 → CSV/Spout;需格式/公式/圖表/多表 → PhpSpreadsheet;舊系統維護 .xls → 遷移至 .xlsx。切勿在 Web 請求中同步生成超大報表,應改為異步任務佇列 + 雲端儲存下載連結。