最稳方案是直接使用 phpspreadsheet:安装 composer require phpoffice/phpspreadsheet,设置中文字体(simsun)和 utf-8 响应头,分批导出防内存溢出,日期字段需设数值类型+excel格式码。

ThinkPHP 6 用 phpspreadsheet 导出 Excel 最稳
别碰 ThinkPHP 自带的 excel 扩展(已废弃)或老版本的 PHPExcel(不维护、有安全风险)。直接装 phpspreadsheet,它是官方推荐继任者,兼容 PHP 7.4+,ThinkPHP 6/7 都能跑。
执行命令安装:
composer require phpoffice/phpspreadsheet
- 导出前确保数据已查出(
$data = Db::table('user')->select();),不要在写入过程中边查边写,容易内存溢出 - 避免用
Db::query()直接拼 SQL 导出——字段名没映射、空值处理麻烦、中文列头易乱码 - 如果数据量超 5000 行,别一次性
->fromArray($data),改用->setCellValue()循环写入 + 每 1000 行调用一次gc_collect_cycles()
中文列头乱码?设置 DefaultFont 和 Content-Type
导出 Excel 后双击打开显示“#VALUE!”或列头是方块,大概率是字体没设对。Excel 默认不认 UTF-8 字体,必须显式指定中文字体(如 SimSun 或 Microsoft YaHei)。
关键两步:
- 设置全局字体:
$spreadsheet->getDefaultStyle()->getFont()->setName('SimSun'); - HTTP 响应头必须加
charset=utf-8:header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8'); - 文件名含中文时,用
rawurlencode()编码:header('Content-Disposition: attachment;filename="'.rawurlencode('用户列表.xlsx').'";');
toArray() 和 getFormattedValue() 别混用
从数据库读出的日期字段(如 datetime 类型)在 Excel 单元格里显示为数字(如 44926),这是 Excel 内部序列值。想让它显示成 “2022-12-31”,不能靠 PHP 格式化字符串再塞进去——会丢失 Excel 的日期格式能力。
- 正确做法:用
$cell->setValueExplicit($timestamp, DataType::TYPE_NUMERIC)写入原始数值,再用$cell->getStyle()->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_DATETIME) - 错误做法:用
$row['created_at']->format('Y-m-d')转成字符串再写入 → Excel 当纯文本,无法排序、筛选、求和 - 批量导出时,
toArray()会把所有值转成字符串,丢掉类型信息;大表导出务必手动控制单元格类型
导出失败报 Allowed memory size exhausted
不是代码写错了,是 PHP 内存不够。ThinkPHP 默认把整张表读进内存再吐给 phpspreadsheet,1 万行 × 20 列就轻松吃掉 128MB。
- 先调高内存限制(临时):
ini_set('memory_limit', '512M');——但只是掩耳盗铃 - 真解法:用游标分批查询 + 流式写入:
$cursor = Db::cursor(['id' => ['>', 0]], ['id', 'name', 'email']); while ($row = $cursor->fetch()) { $sheet->appendRow([$row['id'], $row['name'], $row['email']]); } - 注意:
appendRow()是phpspreadsheet5.0+ 才支持的方法,低于此版本得用setCellValueByColumnAndRow()手动定位
复杂点在于:分批导出要自己维护行号、合并单元格、样式不能跨批次复用。小项目直接调高内存+单次导出更省事;上万行且要加样式,就得切片+缓存中间状态。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











