优化file_put_contents的速度

时间:2018-12-20 14:48:55

标签: php json base64

我正在尝试加快视频文件的创建速度。我的问题是file_put_contents创建平均50mb视频文件花费的时间很长。我下面的代码将首先对视频进行解码,然后将其放入目录中。 $ Video 是一个字节数组。有没有一种方法可以在不进行base64解码的情况下创建视频文件,该怎么办才能加快文件创建速度?

document.querySelector('#load').addEventListener('click', function() {
  var div = document.getElementById('demo');
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "https://api.data.gov.sg/v1/transport/traffic-images", true);
  xhr.onload = function() {
    var response = JSON.parse(this.responseText);
    response.items.forEach(function(item) {
      item.cameras.forEach(function(camera) {
        var img = document.createElement('img');
        img.src = camera.image;
        div.appendChild(img);
        
        var p = document.createElement('p');
        p.innerText = camera.location.latitude + ', ' + camera.location.longitude;
        div.appendChild(p);
      });
    });
  };
  xhr.send();
});

1 个答案:

答案 0 :(得分:1)

媒体文件可能很大,因此您无法使用将整个文件加载到RAM中的技术来处理它,更不用说同时拥有多个数据副本了。您的代码做了几次。

$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
$Video = $json_obj->Video;
$video_decode = base64_decode($Video);

这时,您已在RAM中加载了4个视频副本。对于50 MB的媒体文件,这意味着$video_decode拥有50 MB的数据,而其他变量更大。您还需要累加json_decode()之类的函数使用的内部存储器。要了解正在使用的RAM,可以在每个操作之间插入它:

var_dump(memory_get_usage(true));

此外,现代视频编解码器是高度优化的二进制流。如果将二进制数据转换为纯文本,则大小会增加。 Base64使数据增长4/3。 JSON会增加一些开销,尽管目前还不很严重。


如果必须从头开始设计,我将使用具有二进制格式(multipart/form-data或自定义编码,可能只是文件原样)的POST request,然后只读取输入分块:

如果您希望或需要坚持当前的设计,则需要分块解决所有文件读取和解码操作:

最后但并非最不重要的一点是,永远不要通过注入不受信任的外部输入来创建SQL代码:

$sql = "UPDATE tblCaf SET Video = '$video_dbfilename' WHERE CAFNo = '$CAFNo'";

Use prepared statements instead。大量建议采用其他方法的教程应该早就被烧死了。


更新:似乎小块数据处理(相对于将所有内容加载到内存中)的概念并不像我想的那么简单,因此我将提供可运行的代码来说明差异

首先,让我们生成一个100 MB的文件进行测试:

$test = __DIR__ . '/test.txt';
$fp = fopen($test, 'wb') or die(1);
for ($i = 0; $i < 1839607; $i++) {
    fwrite($fp, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n") or die(2);
}
fclose($fp);

如果您使用易于使用的一线助手功能:

ini_set('memory_limit', '300M');
$test = __DIR__ . '/test.txt';
printf("Current memory usage: %d MB\n", memory_get_usage(true) / 1024 / 1024);
$data = file_get_contents($test);
printf("Current memory usage: %d MB\n", memory_get_usage(true) / 1024 / 1024);
file_put_contents(__DIR__ . '/copy.txt', $data);
printf("Current memory usage: %d MB\n", memory_get_usage(true) / 1024 / 1024);
printf("Maximum memory usage: %d MB\n", memory_get_peak_usage(true) / 1024 / 1024);

...这是以需要大量内存(没有免费食物)为代价的:

Current memory usage: 0 MB
Current memory usage: 100 MB
Current memory usage: 100 MB
Maximum memory usage: 201 MB

如果将文件分成小块:

$test = __DIR__ . '/test.txt';
$chunk_size = 1024 * 1024;
$input = fopen($test, 'rb') or die(1);
$output = fopen(__DIR__ . '/copy.txt', 'wb') or die(2);
printf("Current memory usage: %d MB\n", memory_get_usage(true) / 1024 / 1024);
while (!feof($input)) {
    $chunk = fread($input, $chunk_size) or die(3);
    fwrite($output, $chunk) or die(4);
}
printf("Current memory usage: %d MB\n", memory_get_usage(true) / 1024 / 1024);
fclose($input);
fclose($output);
printf("Current memory usage: %d MB\n", memory_get_usage(true) / 1024 / 1024);
printf("Maximum memory usage: %d MB\n", memory_get_peak_usage(true) / 1024 / 1024);

...您可以为任何大小的文件使用固定数量的内存:

Current memory usage: 0 MB
Current memory usage: 1 MB
Current memory usage: 1 MB
Maximum memory usage: 3 MB