PHP批量导入导出核心在于方式选择、数据量适配、容错机制与前端反馈;小数据(5w行)宜用CLI或队列,CSV导入需分批事务+逐行解析,导出须流式处理并加BOM防乱码。

PHP 批量导入导出数据,核心不在于“能不能做”,而在于“用什么方式做、数据量多大、要不要容错、是否要前端反馈”。小数据(fgetcsv + INSERT INTO ... VALUES (...),(...) 就够;中等数据(1k–5w 行)必须加事务和分批插入;大数据(> 5w 行)建议走 CLI + LOAD DATA INFILE 或队列异步处理。
用 fgetcsv 读 CSV 并批量插入 MySQL,为什么不能一次 mysqli_query 插所有行?
因为内存会爆,SQL 长度超限,且单条语句失败整批回滚不可控。正确做法是:按固定行数(如 500 行)分组,每组开启事务、拼接多值 INSERT、执行后提交。
- 用
fopen($file, 'r')打开文件,避免file_get_contents一次性加载全部内容 - 跳过首行(含表头):用
fgetcsv()读一次丢弃 - 每 500 行调用一次
mysqli_begin_transaction()+ 拼接INSERT INTO t (a,b) VALUES (1,'x'),(2,'y'),... - 插入前对字段值用
mysqli_real_escape_string()或预处理绑定,防止 SQL 注入和格式错乱 - 遇到某行解析失败(如字段数不匹配),记录错误行号和原始行内容到日志,不要中断整个流程
导出大量数据时,为什么不能用 SELECT * FROM t 全查再 fputcsv?
内存溢出、超时、HTTP 连接断开。导出必须流式处理,边查边写,不缓存结果集。
- 用
mysqli_use_result()(非store_result),让 MySQL 逐行返回,PHP 边 fetch 边写入fopen('php://output', 'w') - 设置
set_time_limit(0)和ini_set('memory_limit', '-1')(仅限 CLI 或可信后台) - 导出前发 HTTP 头:
header('Content-Type: text/csv; charset=utf-8');、header('Content-Disposition: attachment; filename="data.csv"'); - 中文字段需在 CSV 第一行前加 BOM:
fpassthru(fopen("php://temp", "r+")); fwrite($fp, "\xEF\xBB\xBF");
LOAD DATA INFILE 导入比 PHP 循环快 10 倍,但为什么本地开发常失败?
因为 MySQL 默认禁用该命令(secure_file_priv 限制路径),且要求文件在服务端磁盘,PHP 上传的文件不在允许目录内。
立即学习“PHP免费学习笔记(深入)”;
- 检查当前权限:
SHOW VARIABLES LIKE 'secure_file_priv';,若返回/var/lib/mysql-files/,则必须把 CSV 放进去(需服务器 root 权限) - 临时启用(仅测试环境):
SET GLOBAL local_infile = ON;,并连接时加mysqli_options($conn, MYSQLI_OPT_LOCAL_INFILE, true) - 字段映射要显式写全:
LOAD DATA INFILE '/var/lib/mysql-files/data.csv' INTO TABLE t FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 ROWS (col1, col2, @var3) SET col3 = STR_TO_DATE(@var3, '%Y-%m-%d') - 注意:Windows 换行是
\r\n,Linux 是\n,LINES TERMINATED BY必须匹配,否则最后一列可能吞掉换行符
真正上线时,别只盯着“功能跑通”。CSV 编码(UTF-8 with BOM?无 BOM?)、空值表示(NULL 字符串还是空白?)、时间字段格式(数据库期望 Y-m-d H:i:s,但 Excel 导出可能是时间戳或文本)、以及用户上传文件名含中文——这些细节出问题,比逻辑错误更难定位。



















