使用Laravel从Web服务器流式传输AmazonS3对象
在我使用Laravel5.1构建的web应用程序中,用户可以上传我存储在AmazonS3中的一些敏感文件。稍后,我希望有权下载此文件的用户。因为我希望这个身份验证检查到位,所以我不能使用传统的方法下载该文件,而是让它们直接链接到S3中的文件 我的做法:使用Laravel从Web服务器流式传输AmazonS3对象,laravel,amazon-s3,laravel-5,flysystem,Laravel,Amazon S3,Laravel 5,Flysystem,在我使用Laravel5.1构建的web应用程序中,用户可以上传我存储在AmazonS3中的一些敏感文件。稍后,我希望有权下载此文件的用户。因为我希望这个身份验证检查到位,所以我不能使用传统的方法下载该文件,而是让它们直接链接到S3中的文件 我的做法: 当用户请求下载时,我的服务器会在本地下载文件,然后流式传输给用户问题:需要很长时间,因为有时文件太大 为用户提供一个预签名的URL,以便直接从S3下载。URL仅在5分钟内有效问题:如果该URL是共享的,任何人都可以在5分钟内下载 ,将数据直接从S
S3Client
对象,因为laravel使用flysystem,我不知道调用什么方法来获取此对象。也许我需要在composer.json中单独包含S3包我相信其他开发人员以前也看到过这样的问题,希望得到一些帮助。如果有人已经使用laravel
Response::download($pathToFile,$name,$headers)
直接从S3流式传输到客户端,那么我很想听听你的方法。通过评论中的讨论,我得出了一些我想分享的要点
预签名URL
正如@ceejayoz所指出的,预签名
URL不是一个坏主意,因为:
云前端
,我还可以限制IP地址,以增加安全性IAM角色
他还指出了另一个不太好的方法,即创建临时IAM用户。如果没有正确执行,这将是一场维护噩梦,因此只有在您知道自己在做什么的情况下才能执行
来自S3的流
这就是我现在选择的方法。也许以后我会转向第一种方法
警告:如果流式传输,则服务器仍然是中间人,所有数据都将通过服务器传输。所以,如果它失败了,或者速度慢了,你的下载速度就会慢
我的第一个问题是如何注册流包装器
:
由于我使用的是Laravel,Laravel使用flysystem进行S3管理,所以我没有简单的方法获得S3客户端。因此,我在我的composer.json
"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完成的,因此需要由我们完成。不是从S3->user流式传输的,是streams S3->laravel->user。您的服务器仍然处于循环中,因此您会占用带宽,并且错过了直接提供S3的大部分好处。@ceejayoz我理解,但我还有什么选择呢。我不能直接访问S3中的文件。另外,为了让其他人下载,我必须使这些文件公开可见。你可能可以拼凑一些东西来限制签名URL在特定IP上的使用。@ceejayoz谢谢你的链接。但是,如果我的用户被授予IAM角色,他们必须是AWS/Amazon的一部分还是必须登录?只要请求开始,就可以完成。顺便说一句,如果您将CloudFront放在S3前面,那么您的签名URL可以限制为IP地址。请参阅-“IpAddress”:{“AWS:SourceIp:“可选IP地址”}
-这将为您的方案增加一些额外的安全性。