php实现班级通信录导入超时怎么办_php分批导入避超时【方案】

14次阅读

fgetcsv 一次性读完再导入会超时,因 php 默认 max_execution_time 为 30 秒,500+ 行 csv 易触发超时错误;且阻塞执行、无法回滚、日志难定位。

php 实现班级通信录导入超时怎么办_php 分批导入避超时【方案】

为什么 fgetcsv 一次性读完再导入会超时

PHP 默认的 max_execution_time 通常为 30 秒,而班级通信录 CSV 文件若含 500+ 行、字段多(如含头像路径、备注等),用 fgetcsv 全量读入内存 + 循环 INSERT,很容易在第 200 行左右触发 Fatal error: Maximum execution time of 30 seconds exceeded。更关键的是:这不仅是“慢”,而是阻塞式执行 —— 中间出错无法回滚,日志难定位,用户看到白屏。

set_time_limit(0) 不是万能解法

强行延长超时看似简单,但实际埋雷:

  • Web 服务器(如 Nginx)有独立的 fastcgi_read_timeout,PHP 层面设了 0,Nginx 可能在 60 秒后直接切断连接,返回 504 Gateway Timeout
  • 共享主机或云函数环境根本不允许调用 set_time_limit,会报 Warning: set_time_limit(): Cannot set max execution time limit
  • 大文件持续占用 PHP 进程,导致并发能力骤降,其他请求排队卡死

分批导入的核心实操要点

真正稳定的方案是「客户端分片 + 服务端流式处理」,不是靠 PHP 硬扛:

  • 前端用 FileReader 分块读取 CSV(例如每次 50 行),通过 AJAX 分多次 POST 到接口,每次携带 offset=0&limit=50 和文件标识符(如 upload_id
  • 后端不依赖 $_FILES 重读文件,而是将上传的 CSV 先存为临时文件(如 /tmp/upload_abc123.csv),每次只 fopen + fseek 定位到指定行号开始读,用 fgetcsvlimit 行后立即 fclose
  • 每批插入前加事务:$pdo->beginTransaction(),成功则 commit,失败 rollback 并返回错误行号,前端可续传
  • 关键参数控制:ini_set('memory_limit', '128M') 防爆内存;stream_set_timeout($fp, 5) 避免文件句柄卡死

示例关键片段:

立即学习 PHP 免费学习笔记(深入)”;

// 每次请求只处理指定范围 $fp = fopen($tempFile, 'r'); fseek($fp, $startPos); // $startPos 来自上一批末尾 ftell() for ($i = 0; $i < $limit && ($row = fgetcsv($fp)) !== false; $i++) {$stmt->execute($row); } $endPos = ftell($fp); // 下次从这里开始 fclose($fp);

别忽略客户端状态同步和断点续传

用户刷新页面、网络中断、手机切后台都会导致上传中断。必须让前后端共同维护进度:

  • 服务端记录 upload_id → {total_lines, processed_lines, status} 到 Redis 或数据库,TTL 设为 2 小时
  • 前端每次请求前先 GET /api/import/status?upload_id=xxx 拉取当前进度,决定从哪一行继续
  • 最后一包提交后,服务端才触发完整校验(如学号重复检查)、生成导入报告,而非每批都查唯一索引拖慢速度

真正的难点不在“怎么分批”,而在“怎么让每次请求都知道自己该干啥、干完后别人还能接上”—— 状态必须显式传递,不能依赖 PHP 进程内存或 session。

text=ZqhQzanResources