小数据用response()->stream()手写csv;中大数据或需excel样式必须用maatwebsite/excel^3.1;导出类中不可调request(),筛选逻辑须在控制器完成,构造时传入已过滤数据。

直接上结论:小数据用 response()->stream() 手写 CSV;中大数据、要样式或 Excel 格式,必须用 maatwebsite/excel ^3.1;别在导出类里调 request(),筛选逻辑全扔控制器里做。
导出 CSV 时中文乱码或内存爆掉怎么办
常见错误是直接 return response($csvString)->header(...) —— 一两千行就可能 OOM,且没加 UTF-8 BOM,Excel 打开中文全是问号。
- 必须用
response()->stream(),流式输出,不缓存整份数据到内存 - 写入第一行前先
fputs($fp, "\xEF\xBB\xBF")(UTF-8 BOM) - 表头和每行数据都走
fputcsv($fp, $row),别手动拼逗号 - 查数据库别用
get(),改用cursor()或chunk(500)分批读 - 每 chunk 后加
gc_collect_cycles(),尤其 PHP 8.1+ 对内存更敏感
maatwebsite/excel 导出类里为什么拿不到 request 参数
因为 collection() 方法可能在队列、命令行、重定向后执行,request() 上下文早已丢失。这不是 Bug,是设计使然。
- 错误写法:
request()->input('date_from')写在collection()里 - 正确做法:控制器里查好数据,构造时传进去,比如
new UsersExport($users) - 导出类只管返回这个已过滤的集合,不碰任何请求逻辑
- 如果必须复用导出类,就让构造函数接收数组或 Collection,别依赖全局状态
Excel 导出后字体是宋体、标题没加粗、列宽挤成一条线
默认样式极简,所有格式都要显式声明,而且 WithStyles 的坐标必须是 Excel 原生格式(如 'A1'),不能写 '1' 或 '0'。
- 强制设字体:在
registerEvents()里用AfterSheet::class回调,调$event->sheet->getStyle('A1:Z1000')->getFont()->setName('Microsoft YaHei') - 标题加粗:配合
WithHeadings,再在styles()里返回['A1:Z1' => ['font' => ['bold' => true]]] - 列宽别靠自动调整(
setAutoSize(true)),大数据量会卡死;改用getColumnDimension('A')->setWidth(20)手动设 - 导出大文件务必加
ShouldQueue接口,否则 HTTP 超时
最易被忽略的是:Laravel 9+ 必须用 maatwebsite/excel ^3.1,装错版本连 ExcelServiceProvider 都找不到;还有导出类里的 collection() 返回值必须是 Illuminate\Support\Collection,不是 Eloquent Collection,也不是数组——少一个 ->collect() 就报错。











