如何用PHP下载大文件?

2023-12-02

我花了一周的时间来找到这个问题的正确答案。 'Right' 我的意思是绝对符合现有的网络标准、可靠且性能有效。最后,我找到了解决方案。

我在 StackOverflow 上找到的所有内容(在 PHP 中可靠地下载大文件, 如何通过PHP脚本下载大文件)不适合我:

  1. 这两种解决方案都不支持范围请求。这使得它们无法用于视频和音频流以及恢复下载;

  2. 所有示例都与缓存和性能无关;

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(使用前将#替换为@)

如何用PHP下载大文件? 的相关文章

随机推荐

  • JQuery Mobile:单击按钮时如何不显示按钮焦点光环?

    我的网络应用程序中有使用 jQuery Mobile 的按钮 单击按钮时 会添加 ui focus 类 该类会在按钮周围显示蓝色光环 该类将保留在那里 直到单击页面上的另一个位置 这种情况发生在 Firefox 中 而不是 iPad 中 我
  • 从视图中删除子查询以使其成为索引视图

    我想创建一个用于全文搜索的索引视图 我面临的唯一问题是子查询 因为索引视图不允许子查询 以下是我的查询 ALTER VIEW dbo Demo with SCHEMABINDING AS select distinct a ID a Tit
  • '' 附近的语法不正确。字符串 ' ' 后面有未闭合的引号

    我只是想知道是否有人可以在这里指出正确的方向 我想我已经看它太久了所以看不到错误 以下代码 SqlCommand updateStyle new SqlCommand UPDATE Lorenz dbo Layout SET bgColou
  • ssrs 报告中的报告参数验证

    I have created one SSRS report having begin date and end date If I provide end date lt start date it will execute the re
  • 将输出通过管道传输到 bash 命令的中间

    与这个问题有些相关 使用ffmpeg将音频文件转换为mp3 我想在 BASH 中使用管道在一行中执行命令 我想做的是 echo Hello somecommand ffmpeg i f mp2 output mp3 哪里的 的输出是some
  • C#,如何创建一个事件并在另一个类中监听它?

    我不知道如何做到这一点 这是示例代码 我想做的事 public Class MainForm Form MyUserControl MyControl new MyUserControl private void Button Click
  • Android kapt java.lang.UnsatisfiedLinkError Room

    我正在更新我的项目以使用 jetpack 库 我在命令行中执行了这个 gradlew app kaptDebugKotlin 在我的项目目录中 并抛出此错误 e kapt An exception occurred java lang Un
  • 带有“链接服务器”的 EF 6.1.3

    我正在使用 SQL Server 2012 和 EF 6 1 3 我有一个中央数据库 A 和另一个链接到数据库 A 的数据库 B 这两个数据库用于两个不同的应用程序 在数据库 B 中 我有一些视图 它们与中央数据库 A 中的某些表完全相同
  • java 9下运行时将jar添加到类路径

    Until java9为了在运行时通过编程方式将外部 jar 添加到类路径 每个人都使用 URLClassLoader sysloader URLClassLoader ClassLoader getSystemClassLoader Me
  • 为什么函数在画布中返回错误的颜色?

    我的画布颜色是 50 255 50 155 当我执行代码时 function getClickedAreaColor x y var data ctx getImageData x y 1 1 data color for var i 0
  • ItemSend 事件未触发

    我有一个 Outlook 2007 加载项 VSTO 任何使用 Outlook 发送的邮件都应在此之前进行修改 我用Application ItemSend事件 如果我直接从 Outlook 发送电子邮件 Inspector 或通过 使用的
  • 如何在 Windows Phone 中的计划任务代理上裁剪图像

    我需要使用 ScheduledTaskAgent 裁剪图像 由于它在后台运行 因此在尝试实例化 WriteableBitmap 时出现跨线程异常 因为它需要在 UI 线程中创建 我有一个图像流 如何在不使用 WriteableBitmap
  • 如何捕获 select2 的 Enter 按键

    我有一个国家 地区的 select2 下拉列表 多选 当用户输入关键字时 它会在菜单中显示相关项目 例如 如果用户输入ind 菜单显示India and 印度尼西亚 如果按下 Enter 键 则第一项 India 被选中 这是默认行为 现在
  • Visual Studio 2015 初始化部分 Nuget.PackageManagement.VisualStudio.VSolutionManager 必须在 UI 线程上调用

    在尝试在最近更新的 Visual Studio 2015 14 0 25431 01 Update 3 中构建或打开项目时 我不断收到有关 nuget 包管理的错误 每当我打开 VS 后第一次构建项目时 都会收到以下错误消息 当我尝试打开
  • 在考虑多个条件的方法中返回 null

    考虑以下方法 private static String method String string if string equals conditionOne return value else if string equals condi
  • 如何在 C++ 中可移植地计算 sha1 哈希值?

    目标是计算SHA1作为 C 程序一部分的一个或多个缓冲区的哈希值 我不确定使用 boost 的 UUID 是否会正确地在哈希值中添加前导零 据我所知 您的字符串应该始终具有相同的长度 因此这里是上面示例的简化版本 可以做到这一点 inclu
  • css3 动画停止

    目前我正在制作带有滑块动画的标题 仅限 css3 http jimmytenbrink nl slider 一切工作正常 除了有时从中心向右移动时滑块会出现故障 看来我需要停止动画几毫秒才能完成 然而 我在互联网上到处搜索 但似乎无法让它工
  • 无法访问 Metro 应用中的资产文件

    我正在尝试读取一个文本文件 该文件作为 Metro 应用程序中的资产提供 如果将文件路径指定为 ms appx Assets file txt 我会收到访问被拒绝错误 显然我需要设置一些访问安装位置文件夹的功能 我尝试启用清单设计器中的所有
  • Visual Studio 安装和部署构建失败,没有错误

    我有一个设置和部署项目 在我们的构建服务器上 在摘要中报告以下内容 全部重建 25 成功 2 失败 0 跳过 我不知道失败的两个是什么 但我相信其中之一是 vdproj 项目 因为如果我在没有安装程序的情况下运行构建 则根本不会报告任何错误
  • 如何用PHP下载大文件?

    我花了一周的时间来找到这个问题的正确答案 Right 我的意思是绝对符合现有的网络标准 可靠且性能有效 最后 我找到了解决方案 我在 StackOverflow 上找到的所有内容 在 PHP 中可靠地下载大文件 如何通过PHP脚本下载大文件