在用PHP 8.1.4更新到Laravel 9.14之后,基于这个答案https://stackoverflow.com/a/52598361/6825499的流媒体已经中断.

我可以看出这是因为

Call to undefined method League\Flysystem\AwsS3V3\AwsS3V3Adapter::getClient()

因此,它似乎已从league/flysystem-aws-s3-v3(3.0.13)的最新版本中删除

我确实找到了这篇SO帖子的参考,它试图解释现在有一个解决方法:Get S3Client from storage facade in Laravel 9

这太复杂了,我无法理解.

有人知道能做什么吗?

推荐答案

经过大量研究,我意识到问题归结为此处所述的更新https://laravel.com/docs/9.x/upgrade#flysystem-3

最后,我稍微修改了代码,使用环境变量来填写代码中适配器之前提供的内容.

在最新版本的flysystem中,不再通过适配器访问客户端,这就是导致服务中断的原因.

对于一个完全的工人阶级,你可以使用下面的laravel 9

<?php
namespace App\Http;

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

class S3FileStream
{
    /**
     * 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 string bucket name
     */
    private $bucket;

    /**
     * @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)
    {
        $options = [
    'region'            => env("AWS_DEFAULT_REGION"),
    'version'           => 'latest'
];
        $this->filePath    = $filePath;
        $this->adapterName = $adapter;
        $this->disk        = Storage::disk($this->adapterName);
        $this->client     = new \Aws\S3\S3Client($options);
        $this->humanName   = $humanName;
        $this->bucket      = env("AWS_BUCKET");
        //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->client->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->bucket}/{$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;
    }
}

Laravel相关问答推荐

在postgres/mysqlс中,我可以定义基于json字段的唯一索引吗?

如何在数据来自Rest API的flutter中创建下拉类别名称列表?

如何将用户对象绑定到中间件中的请求

在 laravel 4.2 中,用户 'root'@'localhost' 的 Laravel 访问被拒绝(使用密码:YES)

所有 POST 请求上的 Laravel 4 CSRF

Laravel 5.7 判断Electron邮件是否经过验证

Laravel Session 总是改变 Laravel 5.4 中的每个刷新/请求

Laravel timestamps() 不会创建 CURRENT_TIMESTAMP

如何仅从 laravel FormRequest 中获取经过验证的数据?

Laravel Nova - 重新排序左侧导航菜单项

Http请求多浏览器的烦恼

Laravel 5 与 Postgresql

防止 Eloquent 查询上的模型水合

无法捕获 Carbon 抛出的异常

Laravel 如何从子域 URL 中删除api前缀

Mac OS X 需要 Mcrypt PHP 扩展

如何判断是否在laravel中使用了分页

如何使用 Laravel 迁移在 Mysql 列中添加注释

Eloquent的关系 - attach附加(但不保存)到 Has Many

在 Laravel 中翻译特定语言