应使用 fgetcsv() 流式逐行读取大 csv 文件,避免 file_get_contents() 一次性加载导致 oom;需处理编码转换、错误检查、预处理批量插入、事务控制及字符集匹配等关键细节。

用 fgetcsv() 逐行读 CSV,别用 file_get_contents() 一次性加载
大文件(比如 50MB+)直接 file_get_contents() 会爆内存,PHP 进程直接 OOM。真实场景里,CSV 往往是导出数据,动辄几万行,必须流式处理。
-
fgetcsv()每次只读一行,内存占用稳定在 KB 级;file_get_contents()+str_getcsv()是把整个文件塞进内存再切,风险极高 - 注意文件编码:如果 CSV 是 GBK/GB2312(尤其 Windows Excel 默认),得先用
mb_convert_encoding()转 UTF-8,否则中文字段入库变乱码 - 用
fopen($file, 'r')后立刻检查返回值,false表示路径错、权限不足或文件被锁,别跳过这步 - 示例关键片段:
$fp = fopen($file, 'r'); if (!$fp) { throw new RuntimeException("无法打开 CSV: $file"); } while (($row = fgetcsv($fp, 0, ',')) !== false) { // 处理 $row } fclose($fp);
批量插入用 PDO::prepare() + execute(),别拼 SQL 字符串
手拼 "INSERT INTO t VALUES ('$a', '$b')" 不仅 SQL 注入风险拉满,性能也差——每条都走一次解析、编译、执行。批量导入时,单条插入万级数据可能要几分钟;预处理+批量 execute 可压到几秒内。
- 预处理语句只编译一次:
$stmt = $pdo->prepare("INSERT INTO user (name, email) VALUES (?, ?)"); - 循环中反复
$stmt->execute([$row[0], $row[1]]),不要在循环里prepare - 若需更高吞吐,可攒 100–500 行做一次多值插入:
INSERT INTO t (a,b) VALUES (?,?),(?,?),...,但要注意 MySQLmax_allowed_packet限制,默认才 4MB - 禁用自动提交:
$pdo->beginTransaction()开头,$pdo->commit()结尾,否则每条 insert 都触发磁盘刷写,慢十倍不止
遇到 SQLSTATE[HY000]: General error: 1366 Incorrect string value 就查三处
这个错不是 PHP 报的,是 MySQL 拦下来的,99% 出现在存 emoji 或生僻中文时,本质是字符集不匹配。
慧博商城系统HuiboShop2011系统特色:1、上百套模板随意下载切换、模板定时更新;2、csv数据导入、数据定向读取,一键导入商品信息,省时、省力; 3、多会员等级管理,一站搞定零售、批发、代销;4、可集成网站分销功能模块,缔造庞大的代理业务链,代理客户一键铺货;5、代码严谨,防SQL注入;前后台用户分开管理,密码不可逆加密;6、简单易操作、只需几分钟搞定一个商城网站;商城后台帐号admin
- 确认 CSV 文件本身编码:用
file -i filename.csv或 VS Code 右下角看,不是“UTF-8 with BOM”就麻烦,BOM 会被当第一列内容读出来 - 确认数据库表字符集:
SHOW CREATE TABLE your_table,必须是utf8mb4,utf8在 MySQL 里实际只支持 3 字节 UTF-8,存不了 emoji - 确认 PDO 连接参数含
;charset=utf8mb4,例如:$dsn = "mysql:host=localhost;dbname=test;charset=utf8mb4";,漏掉这串,连上也是退化成 latin1 - 额外提醒:MySQL 5.7+ 默认
sql_mode含STRICT_TRANS_TABLES,遇到空字符串插非空字段也会报错,得提前清洗数据
跳过表头、处理空行、过滤脏数据这些事得在 PHP 层做干净
别指望数据库替你容错。CSV 常见问题:首行是标题、中间有空行、某列少引号导致字段错位、数字列混了“N/A”字符串——这些全得在 fgetcsv() 后立刻判断,否则一条错,整批滚挂。
立即学习“PHP免费学习笔记(深入)”;
- 用
$headerSkipped = false标记是否跳过首行,if (!$headerSkipped) { $headerSkipped = true; continue; } - 检查
$row是否为false(文件末尾)或empty($row)(空行),直接continue - 对关键字段做类型校验:
filter_var($row[2], FILTER_VALIDATE_EMAIL)或is_numeric($row[1]),不合规的记录记日志、跳过,别硬插 - 日期字段别信 CSV 里的格式,用
DateTime::createFromFormat('Y/m/d', $row[3])显式解析,失败就当无效数据
innodb_buffer_pool_size 和 PHP 的 memory_limit —— 这俩不调,其他都白优化。










