tp5.1大数据导出必须用cursor()流式查询+手动fputcsv输出csv,禁用db::select()和excel导出。需输出bom、正确响应头、清缓冲、显式字段、过滤大字段,并严格控制字段顺序与空值处理。

超时不是 PHP 配置没调够,而是你还在用 Db::select() 查完再导 —— 这种写法在 TP5.1 里必然卡死或超时,必须切到游标流式输出。
为什么 TP5.1 的 cursor() 是唯一靠谱路径
TP5.1 不支持原生 PDOStatement 流式 fetch,但提供了 cursor() 方法,底层调用的是 PDO 的 fetch(PDO::FETCH_ASSOC),每行只占几 KB 内存,10 万行也稳定在 5–8 MB。它不走 ORM 实例化,绕过模型层和自动类型转换,避免 blob 字段反序列化吃光内存。
-
cursor()不支持limit()和page(),分页得靠 SQL 条件:比如where('id > ?', $lastId)+order by id - 必须显式指定字段:
field('id,name,email,created_at'),否则*可能触发额外元数据查询,破坏流稳定性 - 含
text或json字段时,加withoutField('content')过滤掉,防止大字段被自动转字符串塞进内存
导出 CSV 必须写的三行头 + 两行清理
缺任何一行,Excel 打开中文就是乱码、文件损坏或下载失败。
- 开头必须输出 BOM:
echo "\xEF\xBB\xBF";,否则 Windows Excel 默认用 GBK 解码 UTF-8 中文 - 响应头要全且顺序不能错:
header('Content-Type: text/csv; charset=utf-8');、header('Content-Disposition: attachment; filename="export.csv"');、header('Cache-Control: no-cache'); - 输出前必须清缓冲:
ob_end_clean();,否则框架输出的空白、debug 信息会混入 CSV 开头,导致 Excel 无法识别 - 写完后必须
fclose($output),否则部分 Nginx 配置下响应体不完整
fputcsv() 比框架封装更稳,但要注意字段顺序和空值
TP5 自带的 exportCsv() 封装会自动加引号、转义换行符,但字段顺序不可控、空值处理不一致;而原生 fputcsv() 把控制权交还给你。
- 表头和数据行字段顺序必须严格一致,否则列对不上 —— 建议把字段名数组存成常量复用
-
null和空字符串''都会被fputcsv()写成空单元格,业务上需提前转成'NULL'或'N/A' - 字段含逗号、换行符、双引号时,
fputcsv()自动加引号并转义,不用手动处理 - 别用
str_putcsv()拼字符串 —— 它不处理引号转义,CSV 格式极易崩
别碰 Excel,除非真需要公式或样式
TP5.1 里强行导 .xlsx 就是给自己设障:PhpSpreadsheet 写入 1 万行 ≈ 占 200MB 内存,且 fromArray() 分块写入仍需缓存整张 sheet 对象。如果只是“看起来像 Excel”,CSV + 后缀改 .xlsx 是最省事的伪装方案。
- 真要 Excel,必须禁用计算引擎:
$spreadsheet->setCalculationEngine(new \PhpOffice\PhpSpreadsheet\Calculation\Calculation());,否则含公式的模板一打开就卡死 - 禁止逐单元格赋值:
$sheet->setCellValue('A'.$row, $val),改用fromArray($chunk, null, 'A2')批量写入 - 每 500 行调一次
ob_flush(),最后才$writer->save('php://output'),否则缓冲区撑爆
真正难的不是写代码,是判断该不该导 Excel —— 大多数所谓“需要 Excel”的需求,其实只要 CSV 能打开、能排序、能筛选就够了。一旦选错格式路径,后面全是救火。











