内存高效的base64解码

时间:2015-10-23 23:27:29

标签: php memory

我们在应用程序中遇到一些问题,人们将图像粘贴到我们的富文本WYSIWYG中,此时它们作为base64编码的字符串存在。例如:

<img src="data:image/png;base64,iVBORw..." />

提交表单已经提交并处理得很好,但是当我们的应用程序生成包含多个图像的页面时,它会导致PHP达到其内存限制,以及膨胀的页面源等。

我所做的是编写一些代码添加到我们的表单处理器中以提取嵌入的图像,将它们写入文件,然后将URL放在src属性中。问题是,处理图像内存使用量时,峰值是数据大小的4倍,这可能会破坏表单处理器。

我的POC代码:

<?php
function profile($label) {
    printf("%10s %11d %11d\n", $label, memory_get_usage(), memory_get_peak_usage());
}

function handleEmbedded(&$src) {
    $dom = new DOMDocument;
    $dom->loadHTML($src);
    profile('domload');
    $images = $dom->getElementsByTagName('img');
    profile('getimgs');
    foreach ($images as $image) {
        if( strpos($image->getAttribute('src'), 'data:') === 0 ) {
            $image->setAttribute('src', saneImage($image->getAttribute('src')));
        }
    }
    profile('presave');
    $src = $dom->saveHTML();
    profile('postsave');
}

function saneImage($data) {
    $type = explode('/', substr($data, 5, strpos($data, ';')-5))[1];
    $filename = generateFilename('./', 'data_', $type);
    //file_put_contents($filename, base64_decode(substr($data, strpos($data, ';')+8)));
    $fh = fopen($filename, 'w');
    stream_filter_append($fh, 'convert.base64-decode');
    fwrite($fh, substr($data, strpos($data, ';')+8));
    fclose($fh);
    profile('filesaved');
    return $filename;
}

function generateFilename($dir, $prefix, $suffix) {
    $dir = preg_replace('@/$@', '', $dir);
    do {
        $filename = sprintf("%s/%s%s.%s", $dir, $prefix, md5(mt_rand()), $suffix);
    } while( file_exists($filename) );
    return "foo.$suffix";
    return $filename;
}

profile('start');
$src = file_get_contents('derp.txt');
profile('load');
handleEmbedded($src);
profile('end');

输出:

     start      236296      243048
      load     1306264     1325312
   domload     1306640     2378768
   getimgs     1306880     2378768
 filesaved     2371080     4501168
   presave     1307264     4501168
  postsave      244152     4501168
       end      243480     4501168

正如您所看到的,尽管尝试使用流过滤器来削减字节,但在保存文件时内存使用量仍会跳入4MB范围。我认为在后台发生了一些缓冲,如果我只是在文件之间进行转录,我会将数据分成块,但我不知道在这种情况下这是否可行/可取。

我可以在任何地方削减内存使用量吗?

注意:

  • file_put_contents()并且将handleEmbedded()更改为不通过引用传递具有相同的内存使用量。
  • derp.txt包含一段HTML,其中包含一个base64编码的图像。
  • 4MB不是世界末日,但就在昨天有人试图上传一个61MB的JPEG,所以谁知道有人会把它放在一个富文本框中。 :I

1 个答案:

答案 0 :(得分:0)

向诺伯特道歉,在我的心理障碍中打洞:

function saneImage($data) {
    $type = explode('/', substr($data, 5, strpos($data, ';')-5))[1];
    $filename = generateFilename('./', 'data_', $type);
    writefile($filename, $data);
    profile('filesaved');
    return $filename;
}

function writefile($filename, $data) {
    $fh = fopen($filename, 'w');
    stream_filter_append($fh, 'convert.base64-decode');
    $chunksize=12*1024;
    $offset = strpos($data, ';')+8;
    for( $i=0; $chunk=substr($data,($chunksize*$i)+$offset,$chunksize); $i++ ) {
        fwrite($fh, $chunk);
    }
    fclose($fh);
}

输出:

     start      237952      244672
      load     1307920     1327000
   domload     1308296     2380664
   getimgs     1308536     2380664
 filesaved     2372712     2400592
   presave     1308944     2400592
  postsave      245832     2400592
       end      245160     2400592