You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP读取逐行JSON格式大文件并导出为CSV的最优方案

大文件JSON行转CSV的低内存实现方案

现有实现的核心问题是将全量文件数据加载到数组中常驻内存,文件体积达到GB级时会出现内存占用过高甚至溢出的问题。最优解决方案是改用流式处理链路,全程不持有全量数据,做到逐行读取、逐行处理、逐行写入CSV,内存占用稳定在常数级,和文件大小完全无关。

具体实现

1. 读取逻辑改用生成器返回

将原本返回全量数组的读取方法改为返回生成器(Generator),遍历生成器时才会逐行加载数据,不会一次性把所有内容存入内存:

public function read(string $file): \Generator
{
    $fileHandle = fopen($file, "r");
    if ($fileHandle === false) {
        throw new \Exception('Could not get file handle for: ' . $file);
    }

    try {
        while (!feof($fileHandle)) {
            $line = trim(fgets($fileHandle));
            // 跳过空行
            if (empty($line)) {
                continue;
            }
            $user = json_decode($line);
            // 跳过JSON解码失败的异常行,也可根据业务需求加日志或抛出异常
            if (json_last_error() !== JSON_ERROR_NONE) {
                continue;
            }
            yield $user;
        }
    } finally {
        // 无论流程是否异常,都保证关闭文件句柄,避免资源泄漏
        fclose($fileHandle);
    }
}

2. 处理逻辑改为流式写入CSV

不要把所有处理后的数据存入数组再统一导出,而是处理一行就写入一行CSV:

public function processInput(string $inputFile, string $outputFile): void
{
    // 打开CSV写入句柄
    $csvHandle = fopen($outputFile, 'w');
    if ($csvHandle === false) {
        throw new \Exception('Could not open CSV file for write: ' . $outputFile);
    }

    try {
        // 写入CSV表头,按需调整字段
        fputcsv($csvHandle, ['user_id', 'user_name']);
        
        // 遍历生成器逐行处理
        foreach ($this->read($inputFile) as $user) {
            $row = [
                $user->user_id,
                strtoupper($user->user_name)
                // 其他需要处理的字段可在此处扩展
            ];
            fputcsv($csvHandle, $row);
        }
    } finally {
        fclose($csvHandle);
    }
}

优化补充

如果觉得逐行写入CSV IO频率太高,可以增加小批量写入逻辑,平衡IO性能和内存占用:

public function processInput(string $inputFile, string $outputFile, int $batchSize = 100): void
{
    $csvHandle = fopen($outputFile, 'w');
    if ($csvHandle === false) {
        throw new \Exception('Could not open CSV file for write: ' . $outputFile);
    }

    try {
        fputcsv($csvHandle, ['user_id', 'user_name']);
        $batch = [];
        foreach ($this->read($inputFile) as $user) {
            $batch[] = [
                $user->user_id,
                strtoupper($user->user_name)
            ];
            // 攒够指定批次大小再统一写入
            if (count($batch) >= $batchSize) {
                foreach ($batch as $row) {
                    fputcsv($csvHandle, $row);
                }
                $batch = [];
            }
        }
        // 写入最后不足批次大小的剩余数据
        if (!empty($batch)) {
            foreach ($batch as $row) {
                fputcsv($csvHandle, $row);
            }
        }
    } finally {
        fclose($csvHandle);
    }
}

这个方案即使设置批次大小为1000,内存也只会额外存储1000行数据,对整体内存占用几乎没有影响。

内容的提问来源于stack exchange,提问作者user3286692

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 17:06:00