最稳php导出方案是fputcsv生成utf-8 bom csv:用fopen('php://output','w')直出,加fputs($fp,"\xef\xbb\xbf")防中文乱码,配正确content-type与content-disposition响应头。

用 fputcsv 导出纯文本 CSV 最稳
PHP 原生导出 Excel,最靠谱的其实是生成 CSV——它本质是逗号分隔的纯文本,Excel 能直接双击打开,且几乎零兼容问题。别被“Excel”二字带偏,真要发报表给财务或运营,CSV 反而最不容易出错。
常见错误现象:fputcsv 报 Warning: fputcsv() expects parameter 1 to be resource, bool given,通常是 fopen 失败没检查返回值;或者中文乱码,因为没设 UTF-8 BOM。
- 必须用
fopen('php://output', 'w')直接输出,别先写文件再读取,否则内存爆 - 导出前加 BOM:用
fputs($fp, "\xEF\xBB\xBF"),否则 Excel 打开中文列名/内容会乱码 - 字段含逗号、换行或引号时,
fputcsv会自动加引号并转义,不用手动处理 - 避免用
mb_convert_encoding反复转码,数据源已是 UTF-8 就保持原样
别碰 PHPExcel 或 PhpSpreadsheet 除非真要公式/样式
这两个库能生成真正的 .xlsx,但代价明显:内存占用高(万行数据轻松吃掉 256MB+)、执行慢、部署麻烦(依赖多、autoload 冲突常见)。多数导出需求只是“把数据库查出来的几列数据扔进表格”,根本用不上单元格合并或条件格式。
使用场景:只有当你需要写入公式(如 =SUM(A2:A100))、冻结首行、设置列宽、或导出多个 sheet 时,才值得引入 PhpSpreadsheet。
立即学习“PHP免费学习笔记(深入)”;
- 用 Composer 安装后,记得在脚本开头加
ini_set('memory_limit', '512M'),不然大文件直接Fatal error: Allowed memory size exhausted -
setCellValue()每次调用都是对象操作,万行数据循环里别用它逐单元格赋值,改用fromArray()批量写入 - 导出前务必调用
$writer->setPreCalculateFormulas(false),否则公式预计算会拖慢十倍
浏览器下载头必须配对,否则文件名乱码或打不开
导出文件名含中文、或想让浏览器强制下载(而不是在标签页里打开),HTTP 响应头一点都不能错。
常见错误现象:Chrome 下文件名变成 %E4%BD%A0%E5%A5%BD.xlsx;Safari 下点下载没反应;Edge 直接把 CSV 当网页渲染出来。
- 必须同时设置
Content-Type和Content-Disposition,缺一不可 - CSV 推荐用:
header('Content-Type: text/csv; charset=utf-8');+header('Content-Disposition: attachment; filename="data.csv"'); - XLSX 必须用:
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); - 中文文件名要 URL 编码:
rawurlencode('报表2024.csv'),别用urlencode(空格变+号,部分浏览器不认)
大数据量导出卡死?得关缓冲、分批、禁用超时
查 10 万条记录导出,脚本默认 30 秒超时、输出缓冲区满、内存撑爆——不是代码写错了,是 PHP 运行环境没调。
性能影响:不处理的话,用户等 30 秒看到 504 Gateway Timeout,Nginx 日志里全是 upstream timed out。
- 开头加
set_time_limit(0),关掉脚本执行时间限制 - 用
ob_end_clean()清掉已有输出缓冲,再ob_start()开新缓冲(仅 CSV 场景需) - 数据库查询别用
fetchAll()一把抓,改用PDO::FETCH_ASSOC配合while ($row = $stmt->fetch())流式读取 - 每写 1000 行调一次
ob_flush()+flush(),防用户端长时间无响应











