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

如何用PHP解析200MB大型JSON文件?解决内存错误问题

如何用salsify/jsonstreamingparser解析大型JSON文件

我来帮你解决这个问题——用salsify/jsonstreamingparser处理大JSON的关键就是流式解析,而不是像json_decode那样一次性把整个文件塞进内存里。之前你看到的基于内存的示例其实没发挥这个库的核心优势,下面我给你一步步讲正确的用法:

核心思路

这个库是事件驱动的流式解析器,它不需要把整个JSON文件加载到内存,而是逐块读取文件内容,在解析过程中触发对应的事件(比如“对象开始”“键值对”“对象结束”),我们只需要在这些事件里处理数据,处理完就可以释放内存,避免溢出。

具体步骤

1. 先确保安装了库

用Composer安装:

composer require salsify/json-streaming-parser

2. 自定义事件监听器

你需要实现一个监听器类,来响应解析过程中的各种事件,只保留当前处理的少量数据,处理完就清空。比如你的JSON是一个大数组,里面每个元素是一个业务对象,我们可以在每个对象解析完成时立刻处理它(比如写入数据库、输出到文件),而不是把所有对象存在内存里。

示例监听器代码:

use Salsify\JsonStreamingParser\Parser;
use Salsify\JsonStreamingParser\Listener;

class BigJsonProcessor implements Listener
{
    private $currentObj = [];
    private $currentKey;
    private $inTargetObject = false;
    private $objectDepth = 0;

    // 遇到对象开始时触发
    public function startObject()
    {
        $this->objectDepth++;
        // 只处理最外层的对象(如果你的JSON是数组包裹的对象)
        if ($this->objectDepth === 1) {
            $this->currentObj = [];
            $this->inTargetObject = true;
        }
    }

    // 遇到对象结束时触发
    public function endObject()
    {
        $this->objectDepth--;
        if ($this->objectDepth === 0) {
            // 这里是处理单个对象的核心逻辑,处理完就清空内存
            $this->handleSingleObject($this->currentObj);
            $this->inTargetObject = false;
            $this->currentObj = [];
        }
    }

    // 遇到键名时触发
    public function key($key)
    {
        if ($this->inTargetObject) {
            $this->currentKey = $key;
        }
    }

    // 遇到字符串值时触发(如果有数字/布尔值,还需要实现对应的方法,比如numberValue/booleanValue)
    public function value($value)
    {
        if ($this->inTargetObject && isset($this->currentKey)) {
            $this->currentObj[$this->currentKey] = $value;
            $this->currentKey = null;
        }
    }

    // 以下是接口要求的空实现,根据你的JSON结构可以按需扩展
    public function startArray() {}
    public function endArray() {}
    public function whitespace($whitespace) {}
    public function startDocument() {}
    public function endDocument() {}
    public function numberValue($value) {
        if ($this->inTargetObject && isset($this->currentKey)) {
            $this->currentObj[$this->currentKey] = $value;
            $this->currentKey = null;
        }
    }
    public function booleanValue($value) {
        if ($this->inTargetObject && isset($this->currentKey)) {
            $this->currentObj[$this->currentKey] = $value;
            $this->currentKey = null;
        }
    }
    public function nullValue() {
        if ($this->inTargetObject && isset($this->currentKey)) {
            $this->currentObj[$this->currentKey] = null;
            $this->currentKey = null;
        }
    }

    // 自定义处理逻辑:比如写入数据库、追加到文件等
    private function handleSingleObject(array $obj)
    {
        // 示例:把对象追加到输出文件
        file_put_contents('processed_data.txt', json_encode($obj) . PHP_EOL, FILE_APPEND);
        // 或者插入数据库:
        // $pdo->prepare("INSERT INTO your_table (col1, col2) VALUES (:val1, :val2)")->execute($obj);
    }
}

3. 流式读取并解析文件

用PHP的文件流逐块读取大JSON文件,把每一块数据传给解析器,这样内存里永远只存当前处理的小块内容:

// 打开大JSON文件(只读模式)
$fileHandle = fopen('your_200mb.json', 'r');
if (!$fileHandle) {
    die("无法打开目标JSON文件");
}

// 初始化监听器和解析器
$processor = new BigJsonProcessor();
$parser = new Parser($processor);

// 逐块读取,每次读4KB(可以根据机器性能调整,比如8192、16384)
$chunkSize = 4096;
while (!feof($fileHandle)) {
    $chunk = fread($fileHandle, $chunkSize);
    $parser->push($chunk);
}

// 完成解析,清理资源
$parser->finish();
fclose($fileHandle);

关键注意事项

  • 不要一次性读整个文件:这是避免内存溢出的核心,用fread逐块读取,解析器处理完就可以丢弃这块数据。
  • 按需调整监听器逻辑:如果你的JSON是大对象嵌套结构,而不是数组,需要调整监听器里的objectDepth判断,确保只处理你关心的节点。
  • 处理完立刻释放内存:在endObject里处理完单个对象后,一定要清空currentObj,避免内存累积。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:08:24