如何用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
相关产品推荐
相关产品推荐

