PHP导入大型CSV文件失败求助:5万行10MB文件处理问题
处理大型CSV文件导入失败的解决方案
我来帮你搞定这个问题——处理几万行的CSV时,fgetcsv踩坑太正常了,咱们从几个常见的点入手排查:
1. 先调整fgetcsv的行长度参数
你代码里给的1000是每行的最大字符数,如果CSV里有某一行(比如产品名称特别长)超过这个长度,fgetcsv就会读取失败或者截断数据。PHP 5.3+支持把这个参数设为0,让它自动检测行的长度,这样更稳妥:
while (($data = fgetcsv($h, 0, ";")) !== FALSE) { // 处理逻辑 }
2. 确认内存和超时设置真的生效
你说已经加了配置,但可能没放在脚本最开头,或者数值不够。试试在脚本第一行就加上:
// 调整内存限制,256M足够处理10MB的CSV了 ini_set('memory_limit', '256M'); // 设置执行超时为5分钟(300秒),可根据数据库写入速度调整 ini_set('max_execution_time', 300); // 注:CLI模式下超时默认是0,不用改;但web模式必须手动设置
3. 数据库写入才是最大的性能瓶颈!
你现在循环里每一行都单独写数据库,5万行就是5万次INSERT,这绝对会拖慢速度甚至超时。一定要用批量插入,比如攒1000行再一次性提交:
$batchSize = 1000; $batchData = []; // 假设你已经有数据库连接$dbConn if (($h = fopen("my_large.csv", "r")) !== FALSE) { // 处理UTF-8 BOM(如果文件带BOM,会导致第一行读取异常) $bom = fread($h, 3); if ($bom !== "\xEF\xBB\xBF") { rewind($h); } while (($data = fgetcsv($h, 0, ";")) !== FALSE) { // 跳过表头(如果你的CSV有表头,根据实际情况调整) // static $isFirstLine = true; // if ($isFirstLine) { $isFirstLine = false; continue; } // 验证列数是否正确,跳过格式错误的行 if (count($data) !== 12) { error_log("跳过格式错误的行:" . implode(';', $data)); continue; } // 转义数据,避免SQL注入并适配数据库格式 $escapedData = array_map(function($val) use ($dbConn) { return mysqli_real_escape_string($dbConn, $val); }, $data); $batchData[] = "('" . implode("','", $escapedData) . "')"; // 攒够一批就插入数据库 if (count($batchData) >= $batchSize) { $sql = "INSERT INTO your_table (col1, col2, ..., col12) VALUES " . implode(',', $batchData); mysqli_query($dbConn, $sql); $batchData = []; // 清空批次 } } // 插入最后一批不足1000行的数据 if (!empty($batchData)) { $sql = "INSERT INTO your_table (col1, col2, ..., col12) VALUES " . implode(',', $batchData); mysqli_query($dbConn, $sql); } fclose($h); }
4. 排查CSV文件的编码问题
如果你的CSV是GBK等非UTF-8编码,或者带BOM,都会导致fgetcsv读取异常。除了上面的BOM处理,还可以在读取后转编码:
// 读取行后转编码(假设原文件是GBK) $data = array_map(function($val) { return mb_convert_encoding($val, 'UTF-8', 'GBK'); }, $data);
5. 加错误日志定位具体问题
如果还是失败,一定要加错误日志看看哪里出了问题:
// 开启错误日志 error_reporting(E_ALL); ini_set('log_errors', 1); ini_set('error_log', 'csv_import_errors.log'); // 在循环里捕获异常 while (($data = fgetcsv($h, 0, ";")) !== FALSE) { try { // 你的处理逻辑 } catch (Exception $e) { error_log("处理行失败:" . implode(';', $data) . " 错误信息:" . $e->getMessage()); } }
按照上面的步骤调整后,应该就能顺利导入了——核心就是减少数据库交互次数,确保fgetcsv能正确读取每一行,再配置足够的系统资源。
内容的提问来源于stack exchange,提问作者K. B.
相关产品推荐
相关产品推荐

