You need to enable JavaScript to run this app. PHP fputcsv导出MySQL 200万+行CSV时提示文件未完全加载的解决方法
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.02 05:36:05