应使用phpspreadsheet配合uploadedfile实现安全高效的excel批量导入:严格校验文件扩展名与大小,用iofactory::load()加载并设首工作表,toarray()转数组时启用跳空行,分批insertall()入库,关闭自动类型推断并手动处理日期与格式。

直接用 PhpSpreadsheet + UploadedFile + 批处理,别碰已停更的 PHPExcel;否则上线后内存崩、编码乱、扩展报错是常态。
上传文件必须校验扩展名和大小,且不能只靠前端限制
用户绕过 JS 校验上传任意文件是分分钟的事。后端必须重验:extensions 要显式指定 xls 和 xlsx,maxSize 建议设为 5 * 1024 * 1024(5MB)以内,再大就该走分片或后台异步了。
-
UploadedFile::getInstance($model, 'file')拿到实例后,立刻检查$file->extension是否在白名单中,别信$_FILES['file']['type'](浏览器伪造太容易) - 临时文件路径(
$file->tempName)必须用于后续读取,不要先saveAs再打开——多一次磁盘 I/O,还可能因并发写入冲突 - 若用
yii\validators\FileValidator,记得加'checkExtensionByMimeType' => true,防改后缀绕过
读 Excel 时用 IOFactory::load() 而非手动 new Reader
IOFactory 会自动识别 xls/xlsx 并选对 Reader,硬写 new Xlsx() 或 new Xls() 容易错配格式,尤其用户传了旧版 Excel 但代码只认新版时,直接抛 InvalidArgumentException。
- 加载后立刻调
$spreadsheet->setActiveSheetIndex(0),避免默认读取隐藏 sheet 或空 sheet - 用
toArray(null, true, true)转数组:第一个null表示空单元格填null,第二个true表示保留公式计算结果(不是公式文本),第三个true表示跳过空白行——不加这个,Excel 末尾几十行空行全进来,遍历时全null还得过滤 - 别用
getRowIterator()遍历:它默认跳过空行,但“空”的定义模糊(有样式没值也算空),容易漏数据;toArray更稳
大批量导入必须分批写库,否则 PHP 内存爆、MySQL 连接超时
一次性 foreach 全部数据然后 new Model()->save(),1000 行就可能吃光 256MB 内存;上万行基本必挂。Yii 的 batch() 是救星,但要用对。
- 先用
array_chunk($data, 100)把数组切成每批 100 行(太大仍占内存,太小 SQL 太碎),再对每批调Model::insertAll() - 别在循环里反复
new Model:批量插入用原生 SQL 或insertAll,比单条save()快 5–10 倍,也省对象创建开销 - 每批插入后加
Yii::$app->db->getSchema()->refresh();(可选),防长事务锁表;更重要的是,插入失败时能准确定位哪一批出错,而不是整个文件标“导入失败” - 如果数据要走模型验证,把验证逻辑抽成静态方法,在分批前做(如
array_filter($data, [UserImport::class, 'validateRow'])),别让验证和 DB 写入混在同一循环里
中文乱码、日期解析错、数字变科学计数——全是 PhpSpreadsheet 默认行为坑
Excel 里写的“2023-01-01”读出来可能是 45292(Excel 序列号),中文列头变成问号,数字“123456789012”变“1.23456789012E+11”,这些不是你代码写错了,是库的默认配置没关。
- 读之前加
\PhpOffice\PhpSpreadsheet\Settings::setLocale('zh_CN');,否则日期/数字格式按 en_US 解析,中文系统下也无效 - 禁用自动类型推断:
$reader = IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(true);,否则字符串“00123”可能被当数字读成123 - 日期列必须手动转:拿到数值后用
\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value),别直接date() - 导出时若要保留前导零(如工号 00123),单元格样式得设
NumberFormat::FORMAT_TEXT,否则 Excel 自动转数字抹零
真正难的不是读进数据,而是让用户清楚知道哪一行哪一列为什么失败——错误信息要带原始行号、列名、原始值,而不是笼统的“数据格式错误”。校验、转换、入库三阶段的错误必须隔离捕获,不然一个空邮箱导致整批回滚,用户根本没法修。











