我的方法:
>当用户请求下载时,我的服务器在本地下载文件,然后流式传输给用户.问题:需要很长时间,因为文件有时太大.
>为用户提供预先签名的URL,以便直接从S3下载. URL仅在5分钟内有效.问题:如果共享该URL,则任何人都可以在5分钟内下载该URL.
> According to this article,将数据直接从S3流式传输到客户端.这看起来很有希望,但我不知道如何实现这一点.
根据这篇文章,我需要:
>注册流包装 – 这是我的第一个问题,因为我不知道如何获取S3Client对象,因为laravel使用flysystem,我不知道调用什么方法来获取这个对象.也许我需要在我的composer.json中单独包含S3包?
>禁用输出缓冲 – 我是否需要在laravel中执行此操作或laravel是否已经处理过它?
我相信其他开发人员之前已经看过这样的问题,并希望得到一些帮助指针.如果有人已经使用laravel Response :: download($pathToFile,$name,$headers)直接从S3流向客户端,那么我很想听听你的方法.
解决方法
预签名网址
正如@ceejayoz指出的那样,预先签名的URL并不是一个坏主意,因为:
>我可以保持时间低至10秒,这对于任何重定向和开始下载都是完美的,但不足以让链接被共享.
>我之前的理解是下载必须在给定的时间内完成.因此,如果链接在10秒内到期,则必须在此之前进行下载.但@ceejayoz指出事实并非如此.已启动的下载允许完成.
>借助云前端,我还可以限制IP地址,以增加更多安全性.
IAM角色
他还指出了另一种不太好的方法 – 创建临时IAM用户.如果没有正确完成,这是一个维护噩梦,所以只有你知道自己在做什么才行.
来自S3的流
警告:如果您流式传输,那么您的服务器仍然是中间人,所有数据都将通过您的服务器传输.因此,如果它失败,或者速度很慢,那么下载速度会很慢.
我的第一个问题是如何注册流包装器:
由于我使用Laravel和laravel使用flysystem进行S3管理,因此我无法轻松获得S3Client.因此,我在composer.json中为Laravel添加了额外的包AWS SDK
"aws/aws-sdk-PHP-laravel" : "~3.0"
然后我写了我的代码如下:
class FileDelivery extends Command implements SelfHandling { private $client; private $remoteFile; private $bucket; public function __construct($remoteFile) { $this->client = AWS::createClient('s3'); $this->client->registerStreamWrapper(); $this->bucket = 'mybucket'; $this->remoteFile = $remoteFile; } public function handle() { try { // First get the Meta-data of the object. $headers = $this->client->headObject(array( 'Bucket' => $this->bucket,'Key' => $this->remoteFile )); $headers = $headers['@Metadata']; if($headers['statusCode'] !== 200) { throw new S3Exception(); } } catch(S3Exception $e) { return 404; } // return appropriate headers before the stream starts. http_response_code($headers['statusCode']); header("Last-Modified: {$headers['headers']['last-modified']}"); header("ETag: {$headers['headers']['etag']}"); header("Content-Type: {$headers['headers']['content-type']}"); header("Content-Length: {$headers['headers']['content-length']}"); header("Content-Disposition: attachment; filename=\"{$this->filename}\""); // Since file sizes can be too large,// buffers can suffer because they cannot store huge amounts of data. // Thus we disable buffering before stream starts. // We also flush anything pending in buffer. if(ob_get_level()) { ob_end_flush(); } flush(); // Start the stream. readfile("s3://{$this->bucket}/{$this->remoteFile}"); } }
我的第二个问题是我是否需要在laravel中禁用输出缓冲?
答案恕我直言是肯定的.缓冲使数据立即从缓冲区刷新,从而降低内存消耗.由于我们没有使用任何laravel函数将数据卸载到客户端,因此不能通过laravel完成,因此需要由我们完成.