【发布时间】: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 个“小”请求和一个额外的“巨大”请求?