【问题标题】:Why are PHPs "memory_limit" and "max_execution_time" limitations not met when using HTTP Accept-Ranges for serving large files?为什么在使用 HTTP Accept-Ranges 提供大文件时不满足 PHP 的“memory_limit”和“max_execution_time”限制?
【发布时间】:2020-12-29 22:21:27
【问题描述】:

以下脚本用于(评估)向客户端发送大型视频文件。它在后面使用 http 标头 Accept-Ranges。即使在处理大文件(> 2 GB)时,也不会遇到 PHP 限制(为了测试,我设置了小的值,例如 memory_limit=16MB 和 max_execution_time=30)。

我想“理解”后面的上下文,因为 chrome 只显示一个(部分)请求,每隔几秒就会增加“时间”和“大小”,尽管 apache 日志文件中没有显示其他请求。

$file = './videos/' . basename($_GET['video']);

if(!file_exists($file)) return; 

$fp = @fopen($file, 'rb');      
$size   = filesize($file); // File size 
$length = $size;           // Content length
$start  = 0;               // Start byte
$end    = $size - 1;       // End byte  

header('Content-type: video/mp4');
header("Accept-Ranges: 0-$length");
header("Accept-Ranges: bytes"); 

if (isset($_SERVER['HTTP_RANGE'])) {
    $c_start = $start;              
    $c_end   = $end;                
    list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
    if (strpos($range, ',') !== false) {
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header("Content-Range: bytes $start-$end/$size");
        exit;      
    }              
    if ($range == '-') {            
        $c_start = $size - substr($range, 1);
    }else{         
        $range  = explode('-', $range); 
        $c_start = $range[0];           
        $c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
    }
    $c_end = ($c_end > $end) ? $end : $c_end;
    if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { 
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header("Content-Range: bytes $start-$end/$size");
        exit;      
    }
    $start  = $c_start;             
    $end    = $c_end;               
    $length = $end - $start + 1;    
    fseek($fp, $start);             
    header('HTTP/1.1 206 Partial Content');
}   
               
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: ".$length);

$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end) { 
    if ($p + $buffer > $end) {      
        $buffer = $end - $p + 1;        
    }              
    set_time_limit(0);              
    echo fread($fp, $buffer);       
    ob_flush();    
}
fclose($fp);       
exit();

【问题讨论】:

  • 至于内存,底部的while循环是以8KB的块读取文件并将它们直接回显到客户端,因此从内存的角度来看它非常低。如果将其连接成一个字符串,服务器的内存限制将在其中起作用。至于附加请求,据我所知,浏览器不会向服务器创建附加请求,即使广告了一个范围,除了恢复下载的情况(或者如果有人写了一些 JS 来做到这一点)。对于超时,未经测试,我希望 30 秒的限制会中止脚本,是吗?
  • 感谢您的解释!超时不会中止脚本 - 我不明白。我稍后会再次测试。也许对于一个 2GB 的文件,浏览器在 30 秒之后才可以看到中止,因为超时只影响服务器端的处理字节数,而不影响显示的视频客户端的持续时间?
  • 关于超时,我注意到最后几行有一个 set_time_limit(0) ......但是当删除它并将 max_execution_time 设置为 1 时,我无法产生超时。现在我想网络服务器(apache)只记录所有范围请求的一个请求!?
  • 如果 Apache 只记录一个请求,我会感到惊讶,非常惊讶。对更多内容的请求仍然是有效的请求,因此应该记录下来。尝试将一些dies 扔到范围代码中,看看它是否真的首先命中。您首先使用什么来调用范围请求?此外,您可以将set_time_limit 移出循环,只需调用一次。
  • 独立于从 html 5 视频对象“src”中启动第一个请求或直接调用脚本,首先完成 1 - 3 个部分请求(每个大约 1 - 5 MB),然后完成额外的部分请求,获取剩余的 ~ 3 GB。这 4 个请求显示在 apache 日志文件 + chrome 开发者工具中。但是由于最后一个请求的数据量,它不应该打破任何时间限制吗?它不会,即使 set_time_limit 被删除。我不明白这个!另外,我不明白为什么有 1 - 4 个“小”请求和一个额外的“巨大”请求?

标签: php apache http resources


【解决方案1】:

通过使用curl 不带任何范围标头,同时具有max_execution_time=1 并提供 2.893 GB 文件,通过命令行请求脚本来了解更多详细信息。

在同一台机器上(在 7 秒和 678 MB 时中止):

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed  
23 2893M   23  678M    0     0  92.5M      0  0:00:31  0:00:07  0:00:24     0 
curl: (18) transfer closed with 2322893144 bytes remaining to read

在带宽较低的外国机器上(在 49 秒和 599 MB 时中止):

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed  
20 2893M   20  599M    0     0  12.1M      0  0:03:58  0:00:49  0:03:09 10284 
curl: (18) transfer closed with 2404747608 bytes remaining to read

关于这一点,max_execution_time=30 在这种情况下并不是真的“小”,因为它允许传输估计大约 30 * 600 MB(= 18 GB!)。客户端的请求时长与服务端的执行时间完全没有关系。

确实,当从 html5 视频对象中调用脚本时,有几个部分请求完成了 max_execution_time=1(在带宽更低的桌面客户端上,每个请求大约需要 14 分钟才能中止并传输大约 500 MB) .

最后,我只是感到困惑,因为客户端请求持续时间和服务器端执行时间之间存在如此巨大的差异。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-12
    • 1970-01-01
    • 2017-09-29
    • 2017-08-08
    • 2020-01-27
    • 1970-01-01
    • 2013-07-08
    • 2022-01-23
    相关资源
    最近更新 更多