核心函數:export_csv() 通用實現
<?php
/**
* 導出 CSV
* @param array $data 二維數據陣列 [['col1','col2'], ...]
* @param array $title 欄位標題 ['欄位1', '欄位2', ...]
* @param string $filename 文件名(不含副檔名)
* @param bool $download true=瀏覽器下載 false=服務端保存
* @param string $saveDir 服務端保存目錄($download=false時必填)
*/
function exportCsv(array $data, array $title, string $filename = '', bool $download = true, string $saveDir = ''): void
{
if (empty($data) || empty($title)) return;
$filename = $filename ?: date('Y-m-d-H-i-s');
$filename .= '.csv';
// 構建 CSV 內容
$csv = '';
// 標題行
$csv .= '"' . implode('","', $title) . "\"\r\n";
// 數據行
foreach ($data as $row) {
// 雙引號轉義:"" 代表一個雙引號
$row = array_map(fn($v) => str_replace('"', '""', $v), $row);
$csv .= '"' . implode('","', $row) . "\"\r\n";
}
// 編碼轉換:UTF-8 → GBK/CP936 (Excel 兼容)
$csv = mb_convert_encoding($csv, 'CP936', 'UTF-8');
if ($download) {
// 瀏覽器下載
// IE 兼容:URL 編碼文件名
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (stripos($ua, 'MSIE') !== false || stripos($ua, 'Trident') !== false) {
$filename = urlencode($filename);
$filename = str_replace('+', '%20', $filename);
}
header('Content-Type: text/csv; charset=gbk');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
header('Pragma: public');
echo $csv;
} else {
// 服務端保存
if (!$saveDir || !is_dir($saveDir)) {
throw new InvalidArgumentException('保存目錄無效');
}
$path = rtrim($saveDir, '/') . '/' . $filename;
file_put_contents($path, $csv);
}
}
?>
調用示例
1. 瀏覽器直接下載
<?php
$data = [
['111', '商品A', '12', '100%', '23', '50%', '4'],
['222', '商品B', '8', '80%', '15', '40%', '3'],
];
$header = ['參數ID', '參數名稱', '統計次數', '統計百分比', '唯一用戶', '用戶百分比', '人均次數'];
exportCsv($data, $header, '統計報表_20240115', true);
// 瀏覽器彈出下載:統計報表_20240115.csv
?>
2. 服務端保存(定時任務/後台生成)
<?php
$saveDir = '/var/www/data/exports';
exportCsv($data, $header, 'daily_report', false, $saveDir);
// 文件保存至:/var/www/data/exports/daily_report.csv
?>
大數據量優化:分塊輸出避免內存溢出
<?php
function exportCsvLarge(Generator $dataGen, array $title, string $filename): void
{
$filename .= '.csv';
header('Content-Type: text/csv; charset=gbk');
header('Content-Disposition: attachment; filename="' . $filename . '"');
// 直接輸出標題
echo mb_convert_encoding('"' . implode('","', $title) . "\"\r\n", 'CP936', 'UTF-8');
// 分塊輸出數據
foreach ($dataGen as $row) {
$row = array_map(fn($v) => str_replace('"', '""', $v), $row);
echo mb_convert_encoding('"' . implode('","', $row) . "\"\r\n", 'CP936', 'UTF-8');
// 可選:定期 flush
// if ($i % 1000 === 0) flush();
}
}
// 生成器示例:從數據庫游標逐行讀取
function getDataGenerator(PDO $pdo): Generator
{
$stmt = $pdo->query('SELECT id, name, qty FROM products');
while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
yield $row;
}
}
?>
常見坑與解決
| 問題 | 原因 | 解決 |
| Excel 打開中文亂碼 | Excel 默認按系統編碼(GBK)打開 CSV | 輸出前 mb_convert_encoding($csv, 'CP936', 'UTF-8') |
| 字段包含逗號/換行/雙引號 | 破壞 CSV 格式 | 字段用雙引號包裹,內部雙引號轉義為 "" |
| 大文件導出內存溢出 | 一次性構建巨大字符串 | 使用 Generator 分塊輸出,或 fputcsv() 寫入臨時文件 |
| IE 下載文件名亂碼 | IE 不支持 UTF-8 文件名 | urlencode() 文件名並替換 + 為 %20 |