利用瀏覽器將 HTML <table> 識別為 Excel 的特性,通過設置 Content-Type: application/vnd.ms-excel 實現零依賴導出。本文講解 Header 細節、中文編碼陷阱、資料庫查詢導出完整流程與局限性。
header('Content-Type: application/vnd.ms-excel; charset=utf-8');
header('Content-Disposition: attachment; filename="export_' . date('Ymd_His') . '.xls"');
header('Cache-Control: max-age=0');
header('Expires: 0');
vnd.ms-excel:觸發 Excel 關聯(.xls 格式);charset=utf-8:關鍵,防止中文亂碼;Content-Disposition: attachment:強制下載而非瀏覽器打開;<?php
header('Content-Type: application/vnd.ms-excel; charset=utf-8');
header('Content-Disposition: attachment; filename="demo.xls"');
?>
<table border="1">
<tr><td colspan="3" align="center">標題行</td></tr>
<tr><th>ID</th><th>姓名</th><th>年齡</th></tr>
<tr><td>1</td><td>張三</td><td>20</td></tr>
<tr><td>2</td><td>李四</td><td>22</td></tr>
</table>
<?php
// config
$db = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Header
header('Content-Type: application/vnd.ms-excel; charset=utf-8');
header('Content-Disposition: attachment; filename="users_' . date('Ymd') . '.xls"');
header('Cache-Control: max-age=0');
// 查詢
$stmt = $db->query('SELECT id, username, email, created_at FROM users ORDER BY id');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用戶名</th>
<th>郵箱</th>
<th>註冊時間</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $row): ?>
<tr>
<td><?= htmlspecialchars($row['id']) ?></td>
<td><?= htmlspecialchars($row['username']) ?></td>
<td><?= htmlspecialchars($row['email']) ?></td>
<td><?= htmlspecialchars($row['created_at']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
charset=utf-8;charset=utf8mb4;htmlspecialchars() 防止標籤注入破壞結構;iconv('UTF-8', 'GBK//IGNORE', $str) 轉 GBK(老版本 Excel 兼容性更好),但會丟失 Emoji。HTML Table 導出本質是 HTML 片段,Excel 打開時解析渲染。支援:
<td style="background:#ffff00; font-weight:bold;"> 背景色、字體;<td style="mso-number-format:'\#\,\#\#0.00'">1234.56</td> 強制兩位小數;<td>=SUM(A2:A10)</td> Excel 會計算;| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| HTML Table + Header | 零依賴、代碼極簡、支援基本樣式/公式 | 無多工作表、大數據記憶體壓力、非標準 .xls | 簡單報表、導出量 < 5000 行、臨時需求 |
| PhpSpreadsheet | 完整 Excel 功能、多工作表、流式寫入、.xlsx | 依賴 Composer、學習曲線、記憶體佔用高 | 正式報表、複雜樣式、大數據、長期維護 |
| fputcsv | 極快、低記憶體、標準 CSV | 無樣式、無公式、無多工作表、編碼需注意 | 海量數據導出、數據交換、ETL |
| box/spout | 流式讀寫 .xlsx/.ods/.csv、低記憶體、快 | API 較新、功能較 PhpSpreadsheet 少 | 大數據導出、性能敏感 |
header('Content-Type: application/vnd.ms-excel; charset=utf-8');
header('Content-Disposition: attachment; filename="big_export.xls"');
// 表頭
echo '<table border="1"><thead><tr><th>ID</th><th>Name</th></tr></thead><tbody>';
$pdo = new PDO(...);
$stmt = $pdo->query('SELECT id, name FROM huge_table');
$stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($row = $stmt->fetch()) {
echo '<tr><td>' . htmlspecialchars($row['id']) . '</td>';
echo '<td>' . htmlspecialchars($row['name']) . '</td></tr>';
// 定期 flush 避免記憶體峰值
if (($row['id'] % 1000) === 0) flush();
}
echo '</tbody></table>';
htmlspecialchars();HTML Table 導出是「瑞士軍刀」:簡單場景極其高效,複雜場景及時換工具。