phpspreadsheet 是 ThinkPHP6.0 导入 Excel 的唯一推荐方案,需按规范安装、引入类、安全上传、指定 Reader、跳过样式、正确读取行列、转换日期、校验+事务入库,并处理文本型数字。

phpoffice/phpspreadsheet 是当前 ThinkPHP6.0 中导入 Excel 的事实标准方案,PHPExcel 已停止维护,**不要用**。直接上 phpspreadsheet,否则后续会踩到编码、日期解析、大文件内存溢出、XLSX/XLS 兼容性等一堆坑。
安装和基础引入必须做对
执行命令安装(注意:TP6 要求 PHP ≥ 7.2,phpspreadsheet ≥ 1.25):
composer require phpoffice/phpspreadsheet
在控制器或服务类中,只引入真正用到的类,避免 autoload 冗余加载:
-
use PhpOffice\PhpSpreadsheet\IOFactory;—— 必须,用于读取文件 -
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;或use PhpOffice\PhpSpreadsheet\Reader\Xls;—— 按需选其一,比IOFactory::createReader()更明确、更可控 - 不用
setReadDataOnly(true)就别引入Style相关类,省内存
常见错误:Class 'PhpOffice\PhpSpreadsheet\IOFactory' not found,90% 是没执行 composer dump-autoload 或 autoloader 缓存未刷新;确认 vendor/autoload.php 已被 TP6 自动加载。
上传后读取 Excel 文件的正确姿势
不能直接读取 $_FILES 的临时路径并传给 IOFactory::load(),因为权限/路径可能被框架拦截或重写。应先移动到可读目录(如 runtime/upload/),再读取:
立即学习“PHP免费学习笔记(深入)”;
- 检查文件扩展名是否为
xlsx或xls,仅靠 MIME 类型不可信 - 用
move_uploaded_file()安全转移,不要用copy()或直接 open - 读取时指定 reader 类型,避免自动探测失败(比如 .xlsx 文件被误判为 .csv):
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); $reader->setReadDataOnly(true); // 关键:跳过样式、公式,大幅提升速度和稳定性 $spreadsheet = $reader->load($filePath);
若要支持旧版 .xls,换用 \PhpOffice\PhpSpreadsheet\Reader\Xls;混用格式建议先用 pathinfo($filename, PATHINFO_EXTENSION) 判断再实例化对应 reader。
逐行读取数据时最容易崩的几个点
表头通常在第 1 行,数据从第 2 行开始,但 getHighestRow() 可能返回空行或隐藏行,导致循环越界或漏数据:
- 用
$worksheet->getHighestDataRow()替代getHighestRow()(后者含空行) - 列索引从 1 开始,不是 0:
getCellByColumnAndRow(1, $row)对应 A 列,不是A$row - 空单元格返回
null或空字符串,数据库插入前必须做isset()/!empty()判断,否则报错或写入脏数据 - 日期字段默认转成 Excel 序数(如 44562),需用
\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject()转换,否则入库是数字而非时间
示例片段(安全读取用户名和创建时间):
$cellA = $worksheet->getCellByColumnAndRow(1, $row);
$username = $cellA ? trim($cellA->getValue()) : '';
$cellC = $worksheet->getCellByColumnAndRow(3, $row);
$rawDate = $cellC ? $cellC->getValue() : null;
$createTime = is_numeric($rawDate)
? \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($rawDate)->format('Y-m-d H:i:s')
: ($rawDate ?: null);
批量入库前务必加校验和事务
Excel 导入本质是“用户提交的非结构化输入”,不能信任任何字段。跳过校验直接 create(),轻则报错中断,重则注入脏数据或破坏业务逻辑:
- 每行数据进 DB 前,用模型的
validate()或独立验证器检查必填、格式(手机号、邮箱)、长度等 - 使用事务包裹整个导入过程:
Db::transaction(),任一行失败就回滚,避免部分写入 - 单次导入超过 500 行,建议分批(如每 100 行 commit 一次),防止超时或内存耗尽
- 导出错误详情时,记录具体哪一行、哪一列出错(如
"第3行,C列:手机号格式错误"),而不是笼统抛异常
最常被忽略的是:Excel 单元格里看似是数字,但实际是文本格式(尤其带前导零的编号、身份证号),getValue() 默认会转成 int 导致截断。要用 getFormattedValue() 或提前设置 setReadDataOnly(false) + getCellDataType() 判断类型。



















