首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 導入導出 Excel 方案全景:從 CSV 到 PHPExcel/PhpSpreadsheet

     編輯:PHP 相關知識

方案選型總覽

方案適用場景優點缺點推薦指數
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功能較簡(無圖表、複雜公式、樣式受限)⭐⭐⭐⭐ (大數據導出首選)

方案一: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;
?>

方案二:PhpSpreadsheet 完整範例(.xlsx、樣式、多工作表)

安裝

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;
?>

方案三:Box/Spout 串流導出百萬行(記憶體恆定)

安裝

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();
?>

導入(讀取)Excel 通用代碼

<?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();
?>

中文亂碼終極解決方案

  1. PHP 檔案編碼:UTF-8 無 BOM。
  2. 資料庫連線:$pdo->exec("SET NAMES utf8mb4")。
  3. CSV 導出:寫入 UTF-8 BOM (\xEF\xBB\xBF),Excel 才能正確識別編碼。
  4. .xlsx/.ods 由庫自動處理 Unicode,無需額外操作。

效能優化清單

  • 大量寫入使用 fromArray() 批次寫入,避免逐儲存格 setCellValue()。
  • 關閉不需要的功能:$spreadsheet->getActiveSheet()->setCalculateEngine(\PhpOffice\PhpSpreadsheet\Calculation\Calculation::instance()) 若無公式可禁用計算引擎。
  • 超過 5 萬行**強制**改用 Spout 或 CSV 串流,否則記憶體爆炸(PhpSpreadsheet 會將整個試算表載入記憶體)。
  • 生產環境啟用 OpCache、JIT,減少庫類載入開銷。

結語

選型原則:純資料/大數據 → CSV/Spout;需格式/公式/圖表/多表 → PhpSpreadsheet;舊系統維護 .xls → 遷移至 .xlsx。切勿在 Web 請求中同步生成超大報表,應改為異步任務佇列 + 雲端儲存下載連結。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved