CSV导入需流式读取fgetcsv()防内存溢出,处理BOM和GBK编码,慎用Maatwebsite/Excel包,upsert()适合去重更新,insert()分批500条,全局事务+关外键检查,记录行号精准定位错误。

CSV 导入在 Laravel 里不是靠 DB::table()->insert() 硬塞就能稳的——字段映射错、大文件卡死、中文乱码、空值处理漏掉,全得手动兜底。
用 str_getcsv() 还是 fgetcsv()?
直接 file_get_contents() + str_getcsv() 只适合几百行的小 CSV;一过万行就内存爆表。真实项目必须流式读取:fgetcsv() 配合 fopen() 是唯一靠谱选择。
-
fgetcsv()每次只读一行,内存占用恒定,适合 10 万+ 行导入 - 别忘了用
setlocale(LC_ALL, 'zh_CN.UTF-8')或mb_convert_encoding()处理 Windows 记事本导出的 GBK 编码 CSV - 如果 CSV 有 BOM 头,
fgets()先读一行并ltrim($line, "\xEF\xBB\xBF")清掉,否则第一列字段名会带乱码
Laravel 的 Maatwebsite/Excel 包到底要不要用?
它封装了 PHPExcel / PhpSpreadsheet,对 Excel 支持好,但对纯 CSV 反而多一层抽象,容易掩盖底层问题。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 如果你只导 CSV,且要控制每行校验、跳过脏数据、实时反馈进度——直接写原生
fgetcsv()更透明 - 用了
Maatwebsite/Excel却没配'csv' => ['delimiter' => ','],遇到分号分隔的 CSV 就全乱套 - 它的
Import类默认把整张表读进内存,WithChunkReading接口必须显式实现,否则和不用它没区别
批量插入时 DB::table()->insert() 和 upsert() 怎么选?
insert() 快,但不防重复;upsert() 能去重更新,但 MySQL 8.0.19+ / PostgreSQL 才支持,SQLite 不行。
- 有唯一索引(比如
email字段)且需忽略重复:用upsert(),指定$uniqueBy = ['email'] - 只要快插不管重复:
insert()分批 500 条一组,比单条create()快 10 倍以上 - 别在循环里反复调
DB::transaction()——整个导入包一层事务就行,不然锁表时间翻倍 - 用
DB::statement('SET FOREIGN_KEY_CHECKS=0')临时关外键检查,导入完再开,避免关联字段缺失报错
最常被跳过的其实是错误行定位:CSV 第 8427 行少了个逗号,你得知道是哪一行、哪一列、原始内容是什么,而不是只抛个 Undefined offset: 5。留一手 array_merge(['row_number' => $lineNumber], $row),日志里才能真解决问题。

















