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

PHP 原生導出 Excel 兩大經典方案:Tab 分隔流輸出與 HTML 表格偽裝法完整實現

     編輯:PHP 綜合知識

前言

在不依賴第三方庫(如 PhpSpreadsheet)的情況下,PHP 原生導出 Excel 主要有兩種經典方案:Tab 分隔文本流輸出(偽裝 .xls) 和 HTML 表格輸出(偽裝 .xls)。兩者各有優劣,本文將完整呈現兩種方案的核心代碼、關鍵點解析及選型建議。

方案一:Tab 分隔流輸出(輕量、內存友好、適合大數據)

核心原理

設置 HTTP Header 為 application/vnd.ms-excel,直接向 php://output 流寫入 Tab(\t)分隔、換行(\n)分行的純文本內容。Excel 打開時會自動識別為欄位和行。

完整實現代碼

<?php
// 配置
$filename = '導出數據_' . date('YmdHis');
$title = '數據報表';

// 執行導出邏輯(通常由表單提交觸發)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 大數據優化設置
    set_time_limit(0);           // 移除執行時間限制
    ini_set('memory_limit', '512M'); // 適當提高內存限制

    // 關鍵 Header:強制下載、指定編碼、文件名
    header('Content-Type: application/vnd.ms-excel; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . $filename . '.xls"');
    header('Cache-Control: max-age=0');
    header('Pragma: public'); // 兼容舊版 IE

    // 1. 打開輸出流
    $fp = fopen('php://output', 'w');
    if (!$fp) {
        die('無法打開輸出流');
    }

    // 2. 輸出 BOM 頭(解決 Excel 直接雙擊打開 UTF-8 亂碼問題)
    fwrite($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));

    // 3. 輸出表頭
    $headers = ['ID', '姓名', '郵箱', '註冊時間'];
    fwrite($fp, implode("\t", $headers) . "\n");

    // 4. 分批查詢並流式輸出(核心:邊讀邊寫,不佔內存)
    $batchSize = 1000;
    $offset = 0;
    $pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8", 'user', 'pass');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    while (true) {
        $stmt = $pdo->prepare("SELECT id, name, email, created_at FROM users LIMIT :limit OFFSET :offset");
        $stmt->bindValue(':limit', $batchSize, PDO::PARAM_INT);
        $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
        $stmt->execute();
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (empty($rows)) break;

        foreach ($rows as $row) {
            // 數據清洗:移除換行符、Tab 等破壞結構的字符
            $cleanRow = array_map(function($val) {
                $val = str_replace(["\r\n", "\n", "\r", "\t"], ' ', $val);
                return $val;
            }, $row);
            fwrite($fp, implode("\t", $cleanRow) . "\n");
        }

        $offset += $batchSize;
        
        // 定期刷新緩衝區,實時傳輸給瀏覽器
        if ($offset % (10 * $batchSize) === 0) {
            ob_flush();
            flush();
        }
    }

    fclose($fp);
    exit;
}
?>
<!DOCTYPE html>
<html><body>
<form method="post"><button type="submit">導出 Excel (Tab 方案)</button></form>
</body></html>

關鍵點解析

  • BOM 頭 (EF BB BF): 關鍵!否則 Excel 直接雙擊打開 UTF-8 文件會亂碼(導入向導則正常)。
  • 流式分批處理: 使用 LIMIT/OFFSET 或游標分批讀取,配合 fwrite 直接寫入流,**不將全部數據載入內存**,支撐百萬級導出。
  • 字符清洗: 必須替換數據中的 \t, \n, \r,否則會破壞列/行結構。
  • 編碼聲明: Header 中指定 charset=utf-8,配合 BOM,雙擊打開正常。

方案二:HTML 表格偽裝法(樣式豐富、支援合併儲存格、但內存佔用大)

核心原理

輸出包含 Excel 特定 XML 命名空間的 HTML <table>,瀏覽器按 .xls 下載,Excel 打開時解析 HTML 表格結構並識別樣式。

完整實現代碼

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $filename = '樣式報表_' . date('YmdHis');
    
    header('Content-Type: application/vnd.ms-excel; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . $filename . '.xls"');
    header('Cache-Control: max-age=0');
    header('Pragma: public');

    // 輸出 Excel 識別的 HTML 頭部(含 Office 命名空間)
    echo '<html xmlns:o="urn:schemas-microsoft-com:office:office" 
              xmlns:x="urn:schemas-microsoft-com:office:excel" 
              xmlns="https://www.w3.org/TR/REC-html40">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <!--[if gte mso 9]>
        <xml>
            <x:ExcelWorkbook>
                <x:ExcelWorksheets>
                    <x:ExcelWorksheet>
                        <x:Name>報表數據</x:Name>
                        <x:WorksheetOptions>
                            <x:DisplayGridlines/>
                            <x:PrintGridlines/>
                        </x:WorksheetOptions>
                    </x:ExcelWorksheet>
                </x:ExcelWorksheets>
            </x:ExcelWorkbook>
        </xml>
        <![endif]-->
        <style>
            /* 可在此定義 CSS 樣式,Excel 會部分識別 */
            .header { background-color: #4472C4; color: white; font-weight: bold; }
            .text-col { mso-number-format: "\@"; } /* 強制文本格式,防止長數字科學計數法 */
        </style>
    </head>
    <body>
    <table border="1">';

    // 表頭
    echo '<tr>';
    foreach (['ID', '姓名', '身份證號', '備註'] as $col) {
        echo '<th class="header">' . htmlspecialchars($col) . '</th>';
    }
    echo '</tr>';

    // 數據行(此處為演示,實際應分批查詢)
    $pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8", 'user', 'pass');
    $stmt = $pdo->query("SELECT id, name, id_card, remark FROM users LIMIT 5000");
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        echo '<tr>';
        echo '<td>' . htmlspecialchars($row['id']) . '</td>';
        echo '<td>' . htmlspecialchars($row['name']) . '</td>';
        // 長數字/身份證號強制文本格式
        echo '<td class="text-col">' . htmlspecialchars($row['id_card']) . '</td>';
        echo '<td>' . htmlspecialchars($row['remark']) . '</td>';
        echo '</tr>';
    }

    echo '</table></body></html>';
    exit;
}
?>
<!DOCTYPE html>
<html><body>
<form method="post"><button type="submit">導出 Excel (HTML 方案)</button></form>
</body></html>

關鍵點解析

  • Office XML 命名空間: xmlns:x="urn:schemas-microsoft-com:office:excel" 及 <x:ExcelWorkbook> 區塊是 Excel 識別此 HTML 為試算表的關鍵。
  • CSS 樣式支援: 可定義背景色、字體、邊框、關鍵:mso-number-format: "\@" 強制文本格式**,解決長數字(身份證、銀行卡)自動變科學計數法的痛點。
  • htmlspecialchars 必須: 防止數據中含 < > & 破壞 HTML 結構。
  • 內存隱患: 若直接在循環中 echo 拼接巨大 HTML 字符串,極易爆內存。**實際大數據場景建議分批輸出配合 ob_flush/flush,或改用方案一。**

方案對比與選型建議

維度方案一:Tab 分隔流方案二:HTML 表格
內存佔用極低(流式寫入)高(需構建完整 HTML 或大量 echo)
數據量適應百萬行+ 無壓力建議 < 5萬行,否則極易 OOM
樣式支持無(純文本)豐富(顏色、字體、邊框、數字格式)
合併儲存格不支援支援 (rowspan/colspan)
中文亂碼風險需加 BOM 解決UTF-8 Header 通常正常
實現複雜度低中(需處理 HTML/Office XML)
適用場景大數據導出、後台報表、定時任務精美報表、需合併儲存格、強制文本格式場景

現代化建議:超越原生方案

對於生產環境,**強烈建議使用成熟庫**:

  • PhpSpreadsheet (繼承 PHPExcel):** 功能最全,支援 .xlsx/.xls/.csv/ODS,樣式、圖表、公式、讀寫全能,**但大數據極佔內存**。
  • Box/Spout:** 流式讀寫,**極低內存佔用**,支援 .xlsx/.csv/.ods,**大數據導出首選**,速度快、無依賴。
  • Laravel-Excel (基於 PhpSpreadsheet/Spout):** Laravel 專用,API 優雅,隊列導出支援好。

總結

原生導出無依賴、部署簡單。**方案一(Tab 流)是大數據、高性能場景的「瑞士軍刀」**,正確加 BOM、清洗字符、分批流式輸出即可穩健運行;**方案二(HTML 表格)適合中小數據、需樣式/合併儲存格/強制文本格式的報表**,但必須警惕內存陷阱。根據業務數據量、樣式需求、服務器資源選型,或直接升級成熟庫,才是長久之計。

PHP 原生導出 Excel 兩大方案完整解析:Tab 分隔流式輸出(大數據首選)與 HTML 表格偽裝法(樣式豐富),附完整代碼、關鍵點、對比表及現代庫推薦。

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