应使用 StreamedResponse 流式导出万级数据,通过 chunkById 分批查询、fputcsv 写入 php://output、添加 BOM 头防中文乱码,并调大 Nginx/PHP-FPM 超时设置。
直接用 response()->stream() 避免内存爆炸
导出万级以上数据时,response()->download() 会先把整个 CSV 写进内存再吐给浏览器,极易触发 Allowed memory size exhausted。必须改用流式响应,边查边写、边写边发。
关键点是:不拼接完整字符串,不调用 collect()->map()->toArray() 全量加载,而是用 chunkById() 分批拉数据,每次用 fputcsv() 写入 php://output 流。
- 必须在
headers中设置Content-Type: text/csv和Content-Disposition: attachment; filename="xxx.csv" - 务必关闭 Laravel 的输出缓冲(
ob_end_clean()),否则 header 会失效或内容重复 - 不要在流回调里做 Eloquent 关联预加载(
with()),分页 chunk 下关联容易错乱
StreamedResponse 类比原生 response()->stream() 更可控
Laravel 9+ 原生支持 Symfony\Component\HttpFoundation\StreamedResponse,它比 response()->stream() 多一层封装,能更明确地控制状态码、header、回调执行时机。
示例核心逻辑: