应使用 phpspreadsheet 分块读取 excel 并分批插入 mysql:先校验 mime 类型、显式创建 reader、关闭样式解析;再按 500 行迭代读取、映射字段、清洗数据;最后每 400 条启事务插入,避免超时与内存溢出。

你需要把 Excel 表格里的几千条用户数据一次性导入到 MySQL 数据库,但直接调用 Db::insertAll() 一跑就超时、内存爆掉、插到一半失败——问题不在函数本身,而在没控制读取方式、没切片、没关日志、没处理字段映射。
装对库、写对命名空间、配好 PHP 版本
别再搜“ExcelReader”或手动下载 ZIP 放 vendor 目录——TP6 已弃用 vendor() 函数,且无自动注册机制。
执行命令安装官方推荐库:composer require phpoffice/phpspreadsheet。
TP6 要求 PHP ≥ 7.2,而 phpspreadsheet ^1.20+ 强制要求 PHP 7.4+;【版本不匹配会导致类加载静默失败,报错却只显示 Class not found】。
在控制器顶部必须写全命名空间:use PhpOffice\PhpSpreadsheet\IOFactory;,漏掉 PhpOffice\ 前缀必报错。
安全读取上传的 Excel 文件
第一步:获取真实可读路径。别传 $_FILES['file']['tmp_name'] 直接给 IOFactory::load()——PHP 进程可能没权限读该临时文件,且路径可能已被清理。
第二步:校验 MIME 类型。别信 $_FILES['file']['type'],它由浏览器伪造,完全不可靠;改用 finfo_file(finfo_open(FILEINFO_MIME_TYPE), $tmpPath) 获取真实类型。
第三步:只允许 .xlsx 和 .xls,拒绝 .csv——phpspreadsheet 默认不支持 CSV 读取,会直接报错。
第四步:显式创建 Reader,而非用 IOFactory::load() 猜格式。IOFactory::createReader('Xlsx') 更快、更稳、更可控;后缀是 xls 就用 'Xls'。
第五步:调用 $reader->setReadDataOnly(true) 关闭样式和公式解析,省内存又提速。
流式分块读取,避免内存爆炸
方法一:用行迭代器控制每次只读 500 行
① 获取工作表:$worksheet = $spreadsheet->getActiveSheet();
② 设置起始行(跳过表头):$startRow = 2;
③ 每次读 500 行:$rowIterator = $worksheet->getRowIterator($startRow, $startRow + 499);
④ 遍历单元格时加 $cellIterator->setIterateOnlyExistingCells(false),否则空列会被跳过,导致列错位。
⑤ 读完当前批次立刻 unset($row, $cellIterator, $rowData),释放内存。
方法二:不用 toArray() ——万行 Excel 会吃光 512MB 内存,【$worksheet->toArray() 是内存杀手,线上严禁使用】。
字段映射与数据清洗
Excel 表头可能是中文“用户姓名”,而数据库字段是 user_name;某列填了“是/否”,但数据库是 tinyint(1)。不做映射直接插,要么字段错位,要么触发 SQL 错误。
第一行必须是表头,硬编码映射规则示例:
$headerMap = ['用户姓名' => 'user_name', '手机号' => 'mobile', '是否启用' => 'status'];
遍历每行时,用 array_filter($rowData) 判断是否为全空行——只看第一列是否为空会漏判中间列全空的脏数据。
字符串日期如 '2026-04-24' 必须转成 strtotime('2026-04-24') 或 date('Y-m-d H:i:s'),【insertAll() 不做自动类型转换,字段值必须是数据库直接受纳格式】。
分批插入并配独立事务
第一步:用 array_chunk($data, 400) 主动切片——MySQL 默认 max_allowed_packet=4M,按每行平均 200 字节算,500 条就逼近极限。
第二步:每批开启独立事务:Db::startTrans();
第三步:执行插入:Db::name('user')->insertAll($chunk);
第四步:成功则提交:Db::commit();,失败则回滚:Db::rollback();
第五步:全部批次完成后,清空临时文件:unlink($filePath);。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











