PHPExcel(現已併入 PhpSpreadsheet)是 PHP 操作 Excel 的標準庫。本文示範兩種導出模式:輕量級 Excel_XML 類(適合簡單報表)與功能完整的 PhpSpreadsheet(支援樣式、公式、圖表),並解決大數據記憶體溢出、中文檔名編碼、日期格式等實戰痛點。
適用於純資料導出、無樣式需求、記憶體敏感場景。
<?php
require 'php-excel.class.php'; // 輕量類檔
$data = [
1 => ['姓名', '姓氏'],
['Schwarz', 'Oliver'],
['Test', 'Peter'],
];
$xls = new Excel_XML('UTF-8', false, '測試工作表');
$xls->addArray($data);
$xls->generateXML('my-test'); // 直接輸出下載
優點:檔案小、無依賴、速度快。缺點:僅支援 XML 格式(.xls)、無樣式/公式/多工作表。
composer require phpoffice/phpspreadsheet
<?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;
function exportExcel(
string $filename,
array $headers,
array $data,
array $options = []
): void {
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle($options['sheetTitle'] ?? 'Sheet1');
// === 表頭樣式 ===
$headerStyle = [
'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 12],
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '4472C4']],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
];
// 寫入表頭
$col = 'A';
foreach ($headers as $i => $header) {
$cell = $col . '1';
$sheet->setCellValue($cell, $header);
$sheet->getStyle($cell)->applyFromArray($headerStyle);
$col++;
}
// === 數據區樣式 ===
$dataStyle = [
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
];
// 寫入數據
$row = 2;
foreach ($data as $rowData) {
$col = 'A';
foreach ($rowData as $cellValue) {
$cell = $col . $row;
$sheet->setCellValue($cell, $cellValue);
$sheet->getStyle($cell)->applyFromArray($dataStyle);
$col++;
}
$row++;
}
// 自動欄寬
foreach (range('A', $col) as $colLetter) {
$sheet->getColumnDimension($colLetter)->setAutoSize(true);
}
// 凍結首行
$sheet->freezePane('A2');
// 輸出
$writer = new Xlsx($spreadsheet);
$filename = iconv('UTF-8', 'GBK//IGNORE', $filename . '.xlsx'); // 中文檔名相容
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header("Content-Disposition: attachment; filename=\"{$filename}\"");
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit;
}
// === 使用範例 ===
$headers = ['訂單編號', '客戶名稱', '金額', '下單時間', '狀態'];
$data = [
['ORD-2023001', '張三', 1299.00, '2023-05-15 10:30:00', '已出貨'],
['ORD-2023002', '李四', 899.50, '2023-05-15 14:22:00', '處理中'],
// ... 更多資料
];
exportExcel('訂單匯出_20230515', $headers, $data);
導出 10 萬筆以上資料時,預設模式會佔用大量記憶體。解決方案:
$writer = new Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false); // 關閉公式預計算
$writer->setUseDiskCaching(true); // 啟用磁碟快取
$writer->save('php://output');
function exportLargeExcel(string $filename, array $headers, Generator $dataGen): void {
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 寫入表頭(略)
$row = 2;
foreach ($dataGen as $rowData) {
$col = 'A';
foreach ($rowData as $val) {
$sheet->setCellValue($col++ . $row, $val);
}
$row++;
// 每 5000 列刷新一次(可選:分檔下載)
if ($row % 5000 === 0) {
$sheet->getParent()->getWriter('Xlsx')->save('php://output');
// 此處需配合 HTTP 分塊傳輸,較複雜,建議改用 CSV 或資料庫導出工具
}
}
// 最終輸出
}
建議:超過 5 萬列改用 fputcsv 產生 CSV,或前端分頁匯出、後端異步生成檔案提供下載連結。
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文檔名下載亂碼 | 瀏覽器編碼不一 | iconv('UTF-8', 'GBK//IGNORE', $filename) + RFC 5987 格式 filename*=UTF-8''... |
| 長數字變科學記號 | Excel 預設格式 | $sheet->getStyle('A')->getNumberFormat()->setFormatCode('@'); 設為文字格式 |
| 日期顯示為數字 | 未設定格式 | setFormatCode('YYYY-MM-DD HH:MM:SS') 或使用 PHPExcel_Shared_Date::PHPToExcel() |
| 記憶體耗盡 | 大量物件驻留 | 分塊寫入、關閉預計算、啟用磁碟快取、或改用 CSV |
| 公式不計算 | 預設不計算 | $writer->setPreCalculateFormulas(true); 或手動 $sheet->calculateFormula() |
// 新增工作表
$sheet2 = $spreadsheet->createSheet();
$sheet2->setTitle('統計圖');
// 加入圖表
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart(
'chart1',
new \PhpOffice\PhpSpreadsheet\Chart\Title('銷售趨勢'),
null,
[$series],
$xAxis, $yAxis
);
$sheet2->addChart($chart);
// 資料驗證(下拉選單)
$validation = $sheet->getCell('E2')->getDataValidation();
$validation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST);
$validation->setFormula1('"已出貨,處理中,已取消"');
$validation->setAllowBlank(false);
$sheet->getCell('E2')->setDataValidation($validation);
// 複製到整欄
for ($r = 3; $r <= $row; $r++) {
$sheet->getCell("E{$r}")->setDataValidation(clone $validation);
}
PhpSpreadsheet 功能強大但記憶體佔用高。實戰策略:簡單報表用輕量類、常規導出用標準封裝、大數據改 CSV/異步生成、複雜樣式僅在必要時啟用。掌握核心 API 與優化參數,能從容應對各類 Excel 導出需求。