PHP fputcsv导出MySQL 200万+行CSV时提示文件未完全加载的解决方法
核心问题定位
你遇到的加载不全问题拆成两层看就很清楚:
- 首先,弹出来的“File not loaded completely”根本不是你导出的文件有问题,是Excel的固有加载上限:2007及以后版本的Excel单工作表最多能加载1048576行,更早的版本甚至只有65536行的上限,200万行的规模早就超了这个数,Excel会直接把后面的行截掉不显示,和CSV本身能存多少数据、你PHP写的导出逻辑好不好没关系。
- 其次,你现在用的脚本确实有导出不完整的隐患:PDO默认会把整段查询的结果全缓冲到PHP内存里,200万行数据很容易碰到PHP默认的内存上限、30秒执行超时的限制,跑到一半脚本直接崩了,最后导出来的文件本身就是缺数据的。
脚本调整步骤
- 关闭PDO查询缓冲,避免全量结果占满内存
很多人以为写了while循环逐行fetch就是流式读数据,实际上只要PDO默认的缓冲查询没关,所有查询结果早就一次性从MySQL拉到PHP内存里了,循环只是从内存里取数而已,200万行的内存占用完全扛不住。需要在数据库连接后、执行查询前,关闭缓冲模式,让MySQL流式推送结果集,PHP边读边输出,全程只占极少的内存。 - 临时放开PHP运行限制,避免流程中断
在脚本最开头添加运行时配置,取消默认的超时、内存、输出缓冲限制,防止大导出时触发配置阈值中断。 - 优化输出逻辑,降低内存开销
每次读取数据时用更省内存的索引数组格式,每输出固定行数主动刷新输出缓冲区,把内容实时推给客户端,避免内容攒在缓冲区导致断连。同时可以给CSV文件加UTF-8 BOM头,避免Excel打开时中文乱码。 - 修正异常处理逻辑
原脚本的异常捕获块为空,如果执行中出现PDO错误,PHP的错误提示会直接混在CSV输出流里,破坏文件的结构。需要在错误发生时清空已输出的内容,返回正确的错误信息。
调整后的完整脚本
<?php // 基础运行配置调整 ini_set('memory_limit', '-1'); // 生产环境可设置为具体值如1024M,避免无限制占用内存 set_time_limit(0); // 取消脚本执行超时 ob_implicit_flush(true); ob_end_clean(); $filename = 'StockOnHand_' . date('YmdHis') . '.csv'; header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=' . $filename); // 数据库连接 $conn = new PDO("mysql:host=hostname;dbname=dbname;charset=utf8mb4", $DB_USERNAME, $DB_PASSWORD); // 关闭查询缓冲,流式读取结果 $conn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); $sql = " select col1, col2 from table "; try { $stmt = $conn->prepare($sql); $stmt->execute(); $fp = fopen('php://output', 'w'); // 写入UTF-8 BOM头,避免Excel打开中文乱码 fwrite($fp, chr(0xEF) . chr(0xBB) . chr(0xBF)); // 写入表头 fputcsv($fp, ['COL1', 'COL2']); $rowCount = 0; // 用索引数组读取,降低内存占用 while ($row = $stmt->fetch(PDO::FETCH_NUM)) { fputcsv($fp, $row); // 每1000行刷新一次输出缓冲区 if (++$rowCount % 1000 == 0) { flush(); ob_flush(); } } fclose($fp); $stmt = null; $conn = null; } catch (PDOException $e) { // 出错时清空已经输出的缓冲区,返回错误信息 ob_end_clean(); header('Content-Type: text/plain; charset=utf-8'); die('导出失败:' . $e->getMessage()); } exit; ?>
后续验证与优化
- 导出完成后,先在服务器上用
wc -l 导出文件路径命令统计实际行数,和数据库中select count(*) from table的结果做比对,如果数量一致,说明导出流程是完整的,只是Excel无法加载全量数据。 - 如果必须用Excel打开导出文件,可以修改导出逻辑做分卷:每输出90万行(预留冗余空间)就停止当前文件的输出,生成下一个带序号的分卷文件,每个文件的行数控制在Excel的加载阈值以下,就不会弹出截断提示。
- 全量大文件如果只是用于存储、数据迁移,可以用支持大文件的文本编辑器打开,完全可以查看所有行数据。
内容的提问来源于stack exchange,提问作者4 Leave Cover
相关产品推荐
相关产品推荐

