TP6导出Excel必须用原生跳转或表单提交;需正确初始化PhpSpreadsheet、设宋体字体、显式列宽、批量写表头、日期分步设置格式、中文文件名rawurlencode编码,大文件应异步队列处理。

TP6 接口导出 Excel 不能走 AJAX,必须用原生链接跳转或表单提交,否则无法触发浏览器下载行为,且中文名、乱码、日期显示异常等问题会集中爆发。核心是正确初始化 PhpSpreadsheet、处理中文字体与日期格式、设置响应头,并确保控制器方法直接输出流。
基础导出:创建表格并写入数据
在控制器中引入命名空间,新建 Spreadsheet 实例,获取活动工作表:
- 必须使用 SimSun(宋体)作为默认字体,避免“微软雅黑”“黑体”等非 Office 内置字体导致打开时提示替换,影响生产环境稳定性
- 列宽建议显式设置,如
$sheet->getColumnDimension('A')->setWidth(15),防止内容被截断 - 表头可批量写入,例如循环
['A1'=>'ID', 'B1'=>'姓名', 'C1'=>'手机号'],比逐个 setCellValue 更清晰 - 数据行从第 2 行开始写入,每行按列索引(A/B/C…)或坐标(如 'A2')赋值,注意字符串末尾加
"\t"可防止数字被 Excel 自动转为科学计数法
防乱码与日期正确显示
两个关键点常被忽略:
诊断并修复涉及公式、命名范围、Power Query 刷新、数据透视表、VBA/宏保存等问题的 Microsoft Excel XLSX 工作簿
-
中文文件名需 rawurlencode 编码,响应头写成:
Content-Disposition: attachment; filename="'.rawurlencode('报表_'.date('Y-m-d').'.xlsx').'" -
日期字段不能直接 setValue,必须分两步:先用
setValueExplicit(..., TYPE_NUMERIC)写入时间戳,再用setFormatCode(FORMAT_DATE_DATETIME)应用格式码,否则 Excel 显示为 44562 这类序列号
大文件或高并发场景:改用队列异步导出
当数据量超 5000 行或用户并发导出较多时,同步执行易超时、占内存、阻塞请求:
- 前端点击后,仅插入一条下载任务记录到数据库(如
download表),返回任务 ID - 后端用 TP6 队列(如 Redis + think-queue)消费该任务,生成 Excel 并存至本地或 OSS
- 前端轮询接口查状态,状态变为“已完成”后提供下载链接,避免用户长时间等待
- 队列任务中务必关闭调试模式、禁用日志冗余输出,防止内存泄漏
模板填充式导出(推荐用于固定样式报表)
适合带合并单元格、固定表头、条件格式、企业 Logo 的正式报表:
- 提前准备一个 .xlsx 模板文件(如
public/templates/report_template.xlsx) - 用
IOFactory::load($templatePath)加载,保留所有样式和结构 - 数据从指定行开始填充(如第 3 行),用
setCellValueByColumnAndRow($col, $row, $value)精准写入 - 导出前设置响应头,最后调用
$writer->save('php://output')直接输出,不落地临时文件

















