Laravel S3 检索视频以流式传输

2024-05-20

我通过 Laravel 应用程序将视频存储到 Amazon S3。效果很好。但我无法“流式传输”它们。

这是例如 URL:https://website.com/video/342.qt?api_token=a5a18c9f-f5f6-5d66-85e3-aaaaaaa https://website.com/video/342.qt?api_token=a5a18c9f-f5f6-5d66-85e3-aaaaaaa,应该从 S3 返回这部电影“212.DdsqoK1PlL.qt”

It returns this output when calling the URL: Output of Video URL That's the video, but I was expecting it to run directly in the browser, like this video does: https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4

该路由调用此函数,从 S3 磁盘检索非公开文件:

public function document(Document $document)
{
    return Storage::disk('s3')->get($document->path);
}

有效的示例 URL 与我的示例 URL 之间的唯一区别是,示例是 MP4 和我的 .QT,但我也尝试了 MP4,并在浏览器中进行相同的输出;所以没有自动播放视频。

我猜直接播放的电影是流媒体视频?...

我的网站在 Ubuntu 上运行并且也安装了sudo apt-get install vlc.


我个人反对重定向到 S3 URL 的想法。我通过 Laravel php 包装服务器端屏蔽所有 URL。如果其他人遇到类似问题,这是我用来执行此操作的代码。此代码是为使用 Laravel 5.6 从 S3 流式传输视频而编写的(并且包括 HTTP_RANGE 支持,因此它也适用于 iOS)。

我使用下面的类,放置在 App/Http/Responses 中。要使用此类,请创建一个执行此操作的方法(这类似于 getFile 方法):

$filestream = new \App\Http\Responses\S3FileStream('file_path_and_name_within_bucket', 'disk_bucket_name', 'output_file_name_when_downloaded');
return $filestream->output();

运气好的话,您应该可以立即进行流式传输(无需泄露 S3 URL)!

S3FileStream.php:

namespace Http\Responses;

use Exception;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\Response;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;

class S3FileStream
{
    /**
     * @var \League\Flysystem\AwsS3v3\AwsS3Adapter
     */
    private $adapter;

    /**
     * Name of adapter
     *
     * @var string
     */
    private $adapterName;

    /**
     * Storage disk
     *
     * @var FilesystemAdapter
     */
    private $disk;

    /**
     * @var int file end byte
     */
    private $end;

    /**
     * @var string
     */
    private $filePath;

    /**
     * Human-known filename
     *
     * @var string|null
     */
    private $humanName;

    /**
     * @var bool storing if request is a range (or a full file)
     */
    private $isRange = false;

    /**
     * @var int|null length of bytes requested
     */
    private $length = null;

    /**
     * @var array
     */
    private $returnHeaders = [];

    /**
     * @var int file size
     */
    private $size;

    /**
     * @var int start byte
     */
    private $start;

    /**
     * S3FileStream constructor.
     * @param string $filePath
     * @param string $adapter
     * @param string $humanName
     */
    public function __construct(string $filePath, string $adapter = 's3', ?string $humanName = null)
    {
        $this->filePath    = $filePath;
        $this->adapterName = $adapter;
        $this->disk        = Storage::disk($this->adapterName);
        $this->adapter     = $this->disk->getAdapter();
        $this->humanName   = $humanName;
        //Set to zero until setHeadersAndStream is called
        $this->start = 0;
        $this->size  = 0;
        $this->end   = 0;
    }

    /**
     * Output file to client.
     */
    public function output()
    {
        return $this->setHeadersAndStream();
    }

    /**
     * Output headers to client.
     * @return Response|StreamedResponse
     */
    protected function setHeadersAndStream()
    {
        if (!$this->disk->exists($this->filePath)) {
            report(new Exception('S3 File Not Found in S3FileStream - ' . $this->adapterName . ' - ' . $this->disk->path($this->filePath)));
            return response('File Not Found', 404);
        }

        $this->start   = 0;
        $this->size    = $this->disk->size($this->filePath);
        $this->end     = $this->size - 1;
        $this->length  = $this->size;
        $this->isRange = false;

        //Set headers
        $this->returnHeaders = [
            'Last-Modified'       => $this->disk->lastModified($this->filePath),
            'Accept-Ranges'       => 'bytes',
            'Content-Type'        => $this->disk->mimeType($this->filePath),
            'Content-Disposition' => 'inline; filename=' . ($this->humanName ?? basename($this->filePath) . '.' . Arr::last(explode('.', $this->filePath))),
            'Content-Length'      => $this->length,
        ];

        //Handle ranges here
        if (!is_null(request()->server('HTTP_RANGE'))) {
            $cStart = $this->start;
            $cEnd   = $this->end;

            $range = Str::after(request()->server('HTTP_RANGE'), '=');
            if (strpos($range, ',') !== false) {
                return response('416 Requested Range Not Satisfiable', 416, [
                    'Content-Range' => 'bytes */' . $this->size,
                ]);
            }
            if (substr($range, 0, 1) == '-') {
                $cStart = $this->size - intval(substr($range, 1)) - 1;
            } else {
                $range  = explode('-', $range);
                $cStart = intval($range[0]);

                $cEnd = (isset($range[1]) && is_numeric($range[1])) ? intval($range[1]) : $cEnd;
            }

            $cEnd = min($cEnd, $this->size - 1);
            if ($cStart > $cEnd || $cStart > $this->size - 1) {
                return response('416 Requested Range Not Satisfiable', 416, [
                    'Content-Range' => 'bytes */' . $this->size,
                ]);
            }

            $this->start                           = intval($cStart);
            $this->end                             = intval($cEnd);
            $this->length                          = min($this->end - $this->start + 1, $this->size);
            $this->returnHeaders['Content-Length'] = $this->length;
            $this->returnHeaders['Content-Range']  = 'bytes ' . $this->start . '-' . $this->end . '/' . $this->size;
            $this->isRange                         = true;
        }

        return $this->stream();
    }

    /**
     * Stream file to client.
     * @throws Exception
     * @return StreamedResponse
     */
    protected function stream(): StreamedResponse
    {
        $this->adapter->getClient()->registerStreamWrapper();
        // Create a stream context to allow seeking
        $context = stream_context_create([
            's3' => [
                'seekable' => true,
            ],
        ]);
        // Open a stream in read-only mode
        if (!($stream = fopen("s3://{$this->adapter->getBucket()}/{$this->filePath}", 'rb', false, $context))) {
            throw new Exception('Could not open stream for reading export [' . $this->filePath . ']');
        }
        if (isset($this->start) && $this->start > 0) {
            fseek($stream, $this->start, SEEK_SET);
        }

        $remainingBytes = $this->length ?? $this->size;
        $chunkSize      = 100;

        $video = response()->stream(
            function () use ($stream, $remainingBytes, $chunkSize) {
                while (!feof($stream) && $remainingBytes > 0) {
                    $toGrab = min($chunkSize, $remainingBytes);
                    echo fread($stream, $toGrab);
                    $remainingBytes -= $toGrab;
                    flush();
                }
                fclose($stream);
            },
            ($this->isRange ? 206 : 200),
            $this->returnHeaders
        );

        return $video;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Laravel S3 检索视频以流式传输 的相关文章

  • PHP - 解析具有固定列宽的文本文件

    我是 PHP 和 Laravel 的新手 我需要打开文件并解析内容以将它们传递到数据库 文本文件具有固定的列宽 它没有分隔符或标题 我认为使用子字符串并将每个子字符串分配给变量将是正确的方法 但我仍在学习该语言的过程中 我不知道如何实现这一
  • 限制检索的行数 MySql、Laravel

    问题 我的问题是使用 Mysql 或 Laravel 通过查询返回的行数是否有限制 首先 我使用 Laravel 5 2 我试图从日志表中检索数据 该表最近已超过 10k 行 现在从那时起 或大约那个时候 用于检索数据的常用 Laravel
  • 有没有更快的方法将多个文件从 s3 下载到本地文件夹?

    我正在尝试使用 jupyter Notebook 从 s3 存储桶下载 12 000 个文件 预计在 21 小时内完成下载 这是因为每个文件一次下载一个 我们可以并行进行多个下载 以便加快进程吗 目前 我正在使用以下代码下载所有文件 Get
  • Eloquent 准备查询但不执行

    我有一个 客户 表 我正在尝试使用 Laravel Eloquent 使用客户的 id 获取记录 Customer where customer id customer id gt get 然而 当执行此操作时 我检查 MySQL 日志并得
  • Laravel 按特定值对结果进行排序

    我有这行从数据库获取结果的代码 clanMembers gt User find Auth user gt clan id gt where clan id Auth user gt clan id gt orderBy username
  • npm run dev 和 npm run production 之间的区别

    我对 Laravel 和 vue js 很陌生 请让我知道它们之间有什么区别npm run dev and npm run production 这和环境有关系吗 npm run dev创建源映射并且不会缩小 js css 这使得调试和查找
  • Laravel 仅按日期分组并获取计数

    我有交易表 我的表架构 id item name quantity created at 我尝试了很多方法但它不起作用 链接 Laravel Eloquent 获取按天分组的结果 https stackoverflow com questi
  • Laravel - 检查 @yield 是否为空

    如果 yield 有内容 是否可以检查刀片视图 我正在尝试在视图中分配页面标题 section title hi world 所以我想检查主布局视图 类似 对于现在 2018 年以上 查看的人 您可以使用 hasSection name y
  • Laravel 5.3 Schema::create ENUM 字段是 VARCHAR

    我刚刚创建了新的迁移 运行后我看到了我的领域type 不是 ENUM 类型 它有一个改为 VARCHAR 255 类型 Schema create payments function Blueprint table table gt inc
  • 将文件上传到S3的模拟测试用例

    我们如何模拟文件上传到 S3 我尝试过这样的事情 file mock mock MagicMock spec File name FileMock mock patch storages backends s3boto S3BotoStor
  • 自动提取S3中的.gz文件

    我正在尝试找到一种解决方案 以便在 ALB 日志文件自动从 ALB 上传到 S3 时提取 gz 格式的 ALB 日志文件 我的桶结构是这样的 log bucket alb 1 AWSLogs account number elasticlo
  • Laravel 5.3 如何在通知电子邮件中显示用户名

    我正在尝试在通知电子邮件中添加用户的名字 目前 Laravel 通知电子邮件的开头如下 Hello 我想将其更改为 Hello Donald 现在 我有这样的设置 此示例适用于密码重置通知电子邮件 用户型号 public function
  • Laravel 完整日历

    我正在尝试按照本教程进行操作https laravelcode com post laravel full calendar tutorial example using maddhatter laravel fullcalendar ht
  • 模型在自身内部调用自己是一种不好的做法吗?

    这是一个在 Laravel 中使用 Eloquent 的示例 假设我正在开发 CMS 控制器获取路由并通过路由查找页面 该模型提供了一个静态函数 该函数使用路由来找出它正在查找的行的 id 那么模型使用本身执行数据库查询并返回结果 控制器代
  • updateExistingPivot() 不起作用

    我正在尝试像这样更新数据透视表 public function updatePermission id permissionId permissionValue Input get value user User find id perms
  • 从 octobercms 中的非 ajax 表单获取输入值

    我正在尝试构建一个简单的搜索功能 下面是我的搜索表格
  • .htaccess 异常导致主目录出现问题

    这是我的目录结构 localhost or livehost app bootstrap public vendor code demo 这是我的 htaccess
  • Laravel,从 JSON 中删除 null Eloquent 对象属性

    有没有一种优雅的方法从 Eloquent 对象中删除 NULL 值 我的对象嵌套有关系 这个特定的调用可能会长达数千行 所以我尝试这样做的主要原因是为用户节省带宽 但服务器性能也是一个考虑因素 My code data locations
  • 保存多对多关系,同步/附加不存在?

    我有以下两个多对多关系的模型 use Illuminate Database Eloquent Model class Permission extends Model The database table used by the mode
  • Laravel leftJoin 仅右表的最后一条记录

    我是 Laravel 的新手 我有两张桌子 1 产品 2 价格 products id product int p key name varchar prices id price int p key id product int

随机推荐

  • 在网格中查找具有相同值的相邻单元格。想法如何改进这个功能?

    我是 Python 新手 学习了 1 个多月 我尝试创建 Tic Tac Toe 然而 一旦我完成了它 我决定扩展棋盘 从 3x3 到 9x9 具体取决于客户的输入 并通过在棋盘上的任意位置连接 4 个行 列或对角线来获胜 因此 我需要一个
  • VBA 架构技巧 - 宏封装

    我拼凑了 Excel 的概念证明 以从数据库获取数据 并需要将其打包 以便可以将其分发给我们的客户 我的第一次尝试只是将所有代码放入代码模块中 但随后在 Excel 中我可以看到宏列表中的所有模块 而我实际上只想要列表中的主要模块 我猜想我
  • 具有多个实体查找器的通用 spring jpa 存储库

    我的应用程序有超过 250 个表 每个表都有 ID 和名称列 我正在尝试将我们的应用程序从 hibernate 3 迁移到带有 hibernate 5 的 Spring JPA 4 3 在我当前的休眠层中 我有 选项 1 public cl
  • C++ 静态初始化顺序失败

    我目前正在学习C 但遇到了一些麻烦 我开发了一个程序 使用了很多 define 但我想用static const相反 碰撞 类型 范围 所以 我现在有类似的东西 file1 hpp class A public static const s
  • 设置浏览器窗口最小化的最小尺寸限制?

    有没有办法在所有浏览器中手动设置浏览器窗口的最小尺寸 你可以试试 body min width 600px 一旦视口小于 600 像素 您将得到一个水平滚动条 这仅适用于支持 min width CSS 属性的现代浏览器 我认为不可能限制用
  • Linux 上哪个版本的 C++ 库符合“ISO C++ 11”标准?

    目前我的计算机上有 Debian Squeeze AMD64 linux libstdc 5 和 libstdc 6 这些 C 库符合 ISO 标准 C 11 吗 不 它们并不完全符合 但它们有一些元素 stdlibc 上的 C 11 支持
  • 使用 JavaScript 将时间戳 UTC 转换为 IST

    我正在寻找一种合适的方法 使用 JavaScript DateTimeStamp 20160108120040 将时间戳从 UTC 转换为 IST 时间戳来自我的正文请求中的 XML 首先 看一下JavaScript 日期格式 http w
  • 我们可以从 LinkPresentation 框架中的 LPLinkView 中提取图像吗?

    我想在我的应用程序中呈现丰富的链接 并将这些数据发送到我的服务器 我需要访问视图内的图像LPLinkView https developer apple com documentation linkpresentation lplinkvi
  • 如何动态地将 sliderInput 添加到闪亮的应用程序中?

    使用闪亮 我上传一个 csv 文件 并根据列名称 我需要向 ui 添加滑块 sidebarPanel fileInput file1 Upload CSV File to Create a Model accept c text csv t
  • 迭代 NSOrderedSet

    我正在尝试迭代 NSOrderedSet 的实例 像这样的事情 func myFunc var orderedSet NSOrderedSet array 42 43 44 for n in orderedSet NSLog i n 但是
  • 应用内结算切换活动

    啊 应用内结算问题永无止境 我一切正常 可以显示应用内购买的交易页面 如果我完成购买 它会返回到我的应用程序 大约 10 秒后它会验证购买 我会收到该商品 另一方面 如果我在返回应用程序后按后退按钮并切换到另一个活动 它永远不会处理事务 即
  • C++ 概念与 Haskell 类型类有何不同?

    Concepts TS 中的 C 概念最近已合并到 GCC 主干中 概念允许人们通过要求类型满足概念的条件 例如 可比较 来约束通用代码 Haskell 有类型类 我对 Haskell 不太熟悉 概念和类型类如何相关 概念 由概念 TS 定
  • MongoDb 注册类映射

    我有以下代码 我希望 MiscellaneousData 覆盖抽象的 MiscellaneousDataBase 然而 IdMemberMap 总是出现空值 使用独立的 正常 类是可行的 if BsonClassMap IsClassMap
  • 下载Xcode? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我刚刚升级到 OS X Mavericks 我很高兴能够使用所有新的 Mavericks 技术 我只有
  • 应用程序图标不显示在操作栏上

    我按照构建新 Android 项目的说明进行操作 除了操作栏出现问题外 我得到了一个可运行的项目 问题是应用程序图标未显示在操作栏上的应用程序标题旁边 我使用以下配置创建了项目 最低要求 SDK API 8 Android 2 2 Froy
  • 使用 su 和 Expect 脚本登录用户时遇到问题

    我正在为一个班级制作一个网站 您可以使用用户名和密码登录 然后它会将您带到一个显示您在班级中的成绩的页面 该网站正在运行bash脚本 https github com jduga002 rapache 并将托管在用户已有用户名和密码登录的计
  • 如何保护 Chrome 扩展

    Chrome 扩展程序被打包为 zip 存档 设置完成后 它将安装在文件夹中 用户可以访问它 他还可以重写扩展 甚至克隆到新的扩展 如何保护扩展免受用户修改和克隆 我发现了 dll 文件的可能性 可以编译 但它不是很好 如果您有一些专有代码
  • Powershell - 在不安装 Excel 的情况下将 CSV 转换为 XLS

    我有一台自动生成报告的服务器 报告采用 CSV 格式 我需要能够直接加密文件 无需第三方压缩 无 WinZIP 或 WinRAR 我认为最好的想法是将 CSV 转换为 XLS 然后通过 Powershell 密码保护 XLS 文件 不幸的是
  • JSF 2.1 中的 HTML 4 <按钮>

    我想使用以下命令 The JSF
  • Laravel S3 检索视频以流式传输

    我通过 Laravel 应用程序将视频存储到 Amazon S3 效果很好 但我无法 流式传输 它们 这是例如 URL https website com video 342 qt api token a5a18c9f f5f6 5d66