db::cursor()是大数据导出唯一安全入口;csv须用cursor+fputcsv加bom头,禁用封装;excel须用phpspreadsheet分块fromarray写入,禁逐单元格赋值。

Db::cursor() 是大数据导出的唯一安全入口,别碰 Db::select()
TP5 导出 Excel 不是加个包就能跑通的事。内存爆掉、中文变问号、文件打不开——这些问题背后不是代码写错了,而是路径选错了。必须按数据量和格式硬性分路:要 CSV 就彻底放弃 Excel;要 .xlsx 就必须用 PhpSpreadsheet,且不能逐单元格写。
大数据量导出必须用 cursor + fputcsv,禁用任何 Excel 封装
查 5 万行数据进内存再喂给 PhpSpreadsheet,99% 触发 Fatal error: Allowed memory size exhausted。Excel 格式本身不支持流式写入,所有单元格对象都会缓存,1 万行轻松吃掉 300MB 内存。
- 用
Db::name('user')->cursor()替代Db::select(),逐行取,内存恒定在几 MB - 输出前必须加 BOM 头:
echo "\xEF\xBB\xBF";,否则 Excel 默认 ANSI 解码,中文全成问号 - 用原生
fputcsv(),别用框架封装的exportCsv—— 字段顺序、空值转空字符串、引号/换行自动转义都由它兜底 -
ob_end_clean()必须调用,否则响应体开头混入空白或 HTML,CSV 文件就损坏
小批量且硬要 .xlsx:用 PhpSpreadsheet + fromArray() 分块写入
如果业务明确要求公式、条件格式或合并单元格,PhpSpreadsheet 是唯一靠谱选择。但初始化和写入节奏必须卡死,否则照样崩。
- 禁用计算引擎:
$spreadsheet->setCalculationEngine(new \PhpOffice\PhpSpreadsheet\Calculation\Calculation());,否则含公式的文件 CPU 爆表 - 避免
$sheet->setCellValue('A'.$row, $val)这种逐单元格赋值,1 万行 = 1 万个 PHP 对象,内存翻倍;改用fromArray($chunk, null, 'A2')批量写入 - 分块写入:每 500 行调一次
ob_flush()推送缓冲区,$writer->save('php://output')只在最后调一次 - 别用
new \PHPExcel()—— 这是已废弃的旧库,TP5.0 官方也不再兼容;composer require phpoffice/phpspreadsheet是唯一正确安装方式
TP5.0 加载 PhpSpreadsheet 的命名空间写法必须完整
TP5.0 自带类自动加载机制,但 PhpSpreadsheet 的命名空间和目录结构与 TP 规范不完全对齐,直接 use Spreadsheet 会报错。
- 控制器顶部必须写完整命名空间:
use PhpOffice\PhpSpreadsheet\Spreadsheet;和use PhpOffice\PhpSpreadsheet\Writer\Xlsx; - 别在
common.php里封装全局函数 newSpreadsheet实例——每次调用都 new 一遍,对象无法复用,内存泄漏更隐蔽 - 不要把
PhpSpreadsheet类扔进vendor/phpexcel或Library/Vendor下手动加载,Composer 自动注册的 autoloader 才能保证命名空间映射准确
cursor + fputcsv 能无限撑住;Excel 是二进制结构,必须整张表建模完成才能序列化。混用路径,等于在生产环境埋定时炸弹。











