PHP:解析没有内存的巨大Xml

时间:2014-02-27 21:34:33

标签: php xml parsing memory

我想问一下,如果有人知道,是否有可能解析500 MB xml。 问题看起来像那样。我有一个巨大的xml文件,它有很少的节点(可能不是很少,但与其中一个节点比较,很好)) 其中一个节点是附件节点,它是base 64编码的。这个节点有可能大约500 MB。

现在我的问题是有没有可能解码它并写入文件而不会耗尽服务器上的内存?更改限制超过1GB不是这里的解决方案。

我正在看xmlReader,但据我所知,我可以到达我要解析的节点,但后来我需要将节点保存到内存中,这是个坏主意。 我需要将此节点附加到文件而不读取它,但首先我需要解码它。

为了使它更有趣,我可能会得到这样的xml部分,但我打算一个接一个地写入文件。 (将其附加到文件末尾)

1 个答案:

答案 0 :(得分:2)

而不是XMLReader使用XML Parser。它允许你用块来解析xml,所以如果内存效率很高的话。这是一个工作示例,它查找<ATTACHMENT>标记并将其内容解码为文件。处理base64非常简单,只需记住它将每3个字符转换为4个字符的编码字符串,因此只要您提供4个可分割长度的块,就可以连接解码结果。

<?php

class ExtractAttachments {

    private $parser;
    private $tmpFile;
    private $tmpHandle;
    private $buffer;

    private $files = array();

    public function __construct($xml) {
        $this->parser = xml_parser_create('UTF-8');
        xml_set_object($this->parser, $this);
        xml_set_element_handler($this->parser, 'tag_start', 'tag_end');
        xml_set_character_data_handler($this->parser, 'cdata');
        $handle = fopen($xml, 'rb');
        while($string = fread($handle, 4096)) {
            xml_parse($this->parser, $string, false);
        }
        xml_parse($this->parser, '', true);
        fclose($handle);
        xml_parser_free($this->parser);
    }

    public function tag_start($parser, $tag, $attr) {
        if($tag == 'ATTACHMENT') {
            $this->tmpFile = tempnam(__DIR__, 'xml');
            $this->tmpHandle = fopen($this->tmpFile, 'wb');
        }
    }

    public function tag_end($parser, $tag) {
        if($this->tmpHandle) {
            if($this->buffer) {
                fwrite($this->tmpHandle, base64_decode($this->buffer));
                $this->buffer = '';
            }
            fclose($this->tmpHandle);
            $this->tmpHandle = null;
            $this->files[] = $this->tmpFile;
        }
    }

    public function cdata($parser, $data) {
        if ($this->tmpHandle) {
            $data = trim($data);
            if($this->buffer) {
                $data = $this->buffer . $data;
                $this->buffer = '';
            }
            if (0 != ($modulo = strlen($data)%4)) {
                $this->buffer = substr($data, -$modulo);
                $data = substr($data, 0, -$modulo);
            }
            fwrite($this->tmpHandle, base64_decode($data));
        }
    }

    public function getFiles(){
        return $this->files;
    }
}

$xml = new ExtractAttachments('large.xml');
$xml->getFiles();