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

PHPExcel 導出 Excel 完整實戰:從簡易陣列到複雜樣式、大數據優化與常見坑點

     編輯:PHP 相關知識

前言

PHPExcel(現已併入 PhpSpreadsheet)是 PHP 操作 Excel 的標準庫。本文示範兩種導出模式:輕量級 Excel_XML 類(適合簡單報表)與功能完整的 PhpSpreadsheet(支援樣式、公式、圖表),並解決大數據記憶體溢出、中文檔名編碼、日期格式等實戰痛點。

一、輕量方案:Excel_XML 類(零依賴、極簡)

適用於純資料導出、無樣式需求、記憶體敏感場景。

<?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)、無樣式/公式/多工作表。

二、標準方案:PhpSpreadsheet 完整實戰

1. 安裝

composer require phpoffice/phpspreadsheet

2. 核心導出函式封裝

<?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 萬筆以上資料時,預設模式會佔用大量記憶體。解決方案:

1. 使用 ChunkRead/ChunkWrite(PhpSpreadsheet 1.18+)

$writer = new Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false); // 關閉公式預計算
$writer->setUseDiskCaching(true);        // 啟用磁碟快取
$writer->save('php://output');

2. 分批查詢 + 追加寫入(自實現)

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 導出需求。

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