我花了一周的时间来找到这个问题的正确答案。 'Right' 我的意思是绝对符合现有的网络标准、可靠且性能有效。最后,我找到了解决方案。
我在 StackOverflow 上找到的所有内容(在 PHP 中可靠地下载大文件, 如何通过PHP脚本下载大文件)不适合我:
这两种解决方案都不支持范围请求。这使得它们无法用于视频和音频流以及恢复下载;
所有示例都与缓存和性能无关;
PHP 7.0 代码已在桌面版 Chrome、Safari、Opera 和 Firefox 上进行了测试。维瓦尔第测试没有成功。
const STDOUT_CHUNK_SIZE = 128 * 1024; // Buffer size to send data to browser. MUST be less then 1/3 of PHP memory size
const CACHE_EXP_SEC = 1800; // Cache expire time is 30 min.
$fileName = "large_video.mp4";
$contentSize = filesize($fileName);
$isAttachment = false; // false allows to use a file as inline element of web page
// Parse range request. Browser asks for part of file
if (isset($_SERVER["HTTP_RANGE"])) {
list($units, $range) = explode("=", $_SERVER["HTTP_RANGE"], 2);
if ($units !== "bytes") {
http_response_code(416); // Requested Range Not Satisfiable
exit;
}
$range = explode(",", $range, 2)[0]; // Get only first range. You can improve this ;)
list($from, $to) = explode("-", $range, 2);
$to = empty($to) ? ($contentSize - 1) : min(abs((int)$to), ($contentSize - 1));
$from = (empty($from) || $to < abs((int)$from)) ? 0 : max(abs((int)$from), 0);
}
else {
// Request for whole content
$from = 0;
$to = $contentSize - 1;
}
// Set response headers
if ($from > 0 || $to < ($contentSize - 1))
{
http_response_code(206); // Partial Content
header("Content-Type: video/mp4"));
header("Content-Range: bytes $from-$to/$contentSize");
header("Content-Length: " . ($from - $to + 1));
}
else {
$etag = md5($file); // Content is immutable but file name can be changed
if (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && trim($_SERVER["HTTP_IF_NONE_MATCH"]) === $etag) {
http_response_code(304); // Not Modified
setCacheHeaders($etag);
exit;
}
http_response_code(200); // Ok
header("Content-Type: video/mp4"));
header("Content-Length: $contentSize");
if ($isAttachment) header("Content-Disposition: attachment; filename=\"$fileName\"");
else header("Content-Disposition: inline");
header("Accept-Ranges: bytes");
setCacheHeaders($etag);
}
// Send response to client
if ($file = fopen($fileName, "rb")) {
fseek($file, $from);
$counter = $from;
set_time_limit(0);
while (!feof($file) && $counter <= $to) {
$bytesToRead = STDOUT_CHUNK_SIZE;
if ($counter + $bytesToRead > $to) $bytesToRead = $to - $counter + 1;
$data = fread($file, $bytesToRead);
$counter += $bytesToRead;
echo $data;
flush();
}
fclose($file);
function setCacheHeaders(string $etag, bool $cacheEnabled = true, bool $public = true)
{
if ($cacheEnabled) {
header("ETag: $etag");
$scope = $public ? "public" : "private";
$sec = CACHE_EXP_SEC;
$age = ($sec >= 0) ? ", max-age=$sec, s-maxage=$sec" : "";
header("Cache-Control: $scope$age, no-transform");
}
else header("Cache-Control: no-cache, no-store, must-revalidate");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)