TP6 Excel导入需安全读取、稳定解析、高效入库,避开乱码等四大坑:上传用绝对路径并显式保存;大文件禁样式、流式迭代;中文表头映射+日期/文本类型显式控制;分批事务入库并及时清理文件。

TP6 接口实现 Excel 导入,核心是安全读取、稳定解析、高效入库,避开乱码、日期错乱、内存溢出和临时文件失效这四大常见坑。关键不在“能不能读”,而在“怎么读得稳、写得准、扛得住”。
上传与路径处理:必须用绝对路径,别碰 $_FILES['tmp_name']
TP6 在后续请求中可能自动清理 PHP 临时文件,直接传 $_FILES['excel']['tmp_name'] 给 PhpSpreadsheet,大概率报“文件不存在”或空数据。正确做法是:
- 调用
$request->file('excel')获取 File 对象 - 用
move()显式保存到可读目录(如runtime/upload/) - 传入
IOFactory::load()的必须是移动后的绝对路径,可用realpath($savePath)确保 - 上传前校验后缀(
xlsx或xls),不单靠 MIME 类型
读取策略:禁用样式 + 流式迭代,万行以上必选
大文件(尤其超 5000 行)若用 getActiveSheet()->toArray(),极易内存溢出。应主动跳过样式、公式,逐行处理:
- 创建 Reader 时调用
$reader->setReadDataOnly(true) - 按扩展名选择具体 Reader:
Xlsx或Xls,比IOFactory::createReader()更可靠 - 用
$spreadsheet->getActiveSheet()->getRowIterator()遍历,而非先取总行数再 for 循环 - 对每一行,用
$row->getCellIterator()拿单元格值,避免空列干扰
数据清洗:中文表头映射 + 日期/数字类型显式控制
Excel 常见问题不是格式错,而是类型误判。PhpSpreadsheet 默认把日期当数值、文本型数字当字符串,需手动干预:
- 首行是中文标题?先读取第一行,构建字段映射数组,例如
['姓名' => 'name', '入职日期' => 'entry_time'] - 日期列不能直接
getValue():先用getFormattedValue()或判断单元格类型,再用DateTime::createFromFormat()转时间戳 - 身份证、工号等文本型数字,Excel 可能自动转科学计数或截断,读取时加单引号前缀或强制转字符串:
(string)$cell->getValue() - 空单元格统一转为
null或默认值,避免数据库 NOT NULL 报错
入库与收尾:事务分批 + 文件及时清理
一次性插入上万条易锁表或超时,且失败后难回滚。推荐分批 + 事务 + 清理闭环:
- 每 500~1000 行启一个事务,
Db::transaction()包裹insertAll() - 单条插入失败时记录日志(含行号、原始值),不中断整个流程
- 导入完成后,立即
unlink($filePath)删除已读取的临时文件 - 返回结构化结果:成功数、失败数、错误详情(非堆栈),前端可展示明细表格











