TP8用PhpSpreadsheet导出大文件内存溢出,根本原因在于其默认MemoryCache策略为每个单元格新建Cell实例,10万行×20列约200万个对象,在PHP 8.0+下轻松超512MB内存,触发Fatal error或超时返回空文件;调高memory_limit治标不治本,且setCacheEnabled(false)仅禁用单元格缓存,样式、公式等仍占内存。

为什么 TP8 用 PhpSpreadsheet 导出大文件会内存溢出
不是数据量本身的问题,而是默认缓存策略把所有单元格对象全留在内存里。PhpSpreadsheet 默认启用 MemoryCache,每写一个单元格就 new 一个 Cell 实例,10 万行 × 20 列 ≈ 200 万个对象,PHP 8.0+ 下轻松吃掉 512MB 以上内存。
常见现象:Fatal error: Allowed memory size of 536870912 bytes exhausted 或请求超时后返回空文件。
- 别信“调高
memory_limit就能解决”——治标不治本,线上环境不可控 -
setCacheEnabled(false)不是万能解药,它只禁用单元格缓存,但样式、公式、合并区域等仍占内存 - TP8 的容器和中间件可能隐式开启输出缓冲(
ob_start),导出前没清理会导致 header 失效、文件损坏
闭包写入(Chunked Write)怎么写才真正分批
所谓“闭包写入”,本质是控制数据读取与写入节奏,避免一次性 toArray() 加载全部结果集。关键不在 PHP 语法的闭包,而在数据库查询 + 写入逻辑的耦合拆解。
正确做法是:每次从 DB 拉 500–1000 行,写完立即 flush 到 php://output,再清空当前 Worksheet 引用(不是 unset($sheet),而是重置行号并复用对象)。
立即学习“PHP免费学习笔记(深入)”;
- 用
Db::cursor()或原生 PDOfetch()避免 Laravel/TP 的 Collection 全量加载 - 不要在循环里反复调用
$spreadsheet->createSheet()—— 每个 sheet 都是独立内存块 - 表头只写一次,在首次 chunk 前完成;后续 chunk 只追加数据行,用
$sheet->fromArray($chunk, NULL, 'A' . ($startRow)) - 禁用自动过滤器:
$sheet->setAutoFilter('A1:Z1')放在表头后,否则每 chunk 都会叠加重复 filter 区域
导出中文且不乱码的三个硬性条件
中文乱码不是字体名写错那么简单,而是 HTTP 层、Excel 文件层、渲染层三者必须对齐。缺一不可。
- 响应头必须带
charset=utf-8:header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8') - 文件内中文字体必须显式设置:
$sheet->getStyle('A1:Z1000')->getFont()->setName('SimSun')(Windows)或'Noto Sans CJK SC'(Linux/macOS),不能依赖默认 DejaVu - PHP 源文件不能含 BOM —— 即使 UTF-8 编码,带 BOM 也会让
header()失效,导致浏览器接收纯二进制流而识别为乱码
漏掉任意一项,都会出现“下载文件打开是方框”或“部分中文变问号”的情况。
TP8 中最容易被忽略的 Writer 输出陷阱
很多人卡在最后一步:明明生成了 Spreadsheet,却下载不了或文件打不开。问题往往出在 Writer 和输出流的配合上。
- 必须用
php://output直接输出,别先 save 到临时文件再 readfile —— 多一次 IO 就多一次内存峰值 - 调用
$writer->save('php://output')前,务必执行:ob_end_clean()、flush()、exit,否则框架中间件可能追加 HTML 内容到 Excel 流里 -
XlsxWriter 比Csv更耗内存,但支持样式;若只要数据,优先用CsvWriter,它天生流式、零样式开销 - TP8 的
Response类不兼容php://output,别试图用return response()->stream(...)—— 直接原生header()+echo更可靠
真正难的不是写几行代码,而是每一处都得和 PHP 运行时、Web 服务器、Excel 解析器三方博弈。稍有松懈,就会在生产环境凌晨三点收到报警。



















