7

在我使用 laravel 5.1 构建的 Web 应用程序中,用户可以上传一些我存储在 Amazon S3 中的敏感文件。稍后我希望有权限的用户下载此文件。由于我希望进行此身份验证检查,因此我无法使用传统方法通过直接链接到 S3 中的文件来下载文件。

我的做法:

  1. 当用户请求下载时,我的服务器在本地下载文件,然后流式传输给用户。问题:需要很长时间,因为文件有时太大。

  2. 为用户提供一个预签名的 URL,以便直接从 S3 下载。URL 仅在 5 分钟内有效。问题:如果该 URL 是共享的,任何人都可以在 5 分钟内下载它。

  3. 根据这篇文章,将数据直接从 S3 流式传输到客户端。这看起来很有希望,但我不知道如何实现。

根据这篇文章,我需要:

  1. 注册流包装器 -这是我的第一个问题,因为我不知道如何获取S3Client对象,因为 laravel 使用 flysystem,而且我不知道调用什么方法来获取这个对象。也许我需要在我的 composer.json 中单独包含 S3 包?
  2. 禁用输出缓冲 -我需要在 laravel 中执行此操作还是 laravel 已经处理了它?

我相信其他开发人员之前已经看到过这样的问题,并希望获得一些帮助。如果有人已经使用 laravel 直接从 S3 流式传输到客户端Response::download($pathToFile, $name, $headers),那么我很想听听您的方法。

4

1 回答 1

9

从评论中的讨论中,我得出了一些我想分享的关键点。

Pre-Signed URLs

正如@ceejayoz 指出的那样,pre-signedURL 不是一个坏主意,因为:

  1. 我可以将时间保持在 10 秒,这对于任何重定向和开始下载来说都是完美的,但不足以共享链接。
  2. 我之前的理解是下载必须在给定的时间内完成。因此,如果链接在 10 秒后过期,则必须在此之前进行下载。但@ceejayoz 指出事实并非如此。允许完成已开始的下载。
  3. 使用cloud front,我还可以限制 IP 地址,以增加安全性。


IAM Roles

他还指出了另一种不太好的方法——创建临时 IAM 用户。如果没有正确完成,这将是一场维护噩梦,所以只有在您知道自己在做什么的情况下才这样做。


Stream From S3

这是我目前选择的方法。也许稍后我会转向第一种方法。

警告:如果您流式传输,那么您的服务器仍然是中间人,所有数据都将通过您的服务器。因此,如果它失败或速度很慢,您的下载速度就会很慢。

我的第一个问题是 how to register stream wrapper

由于我使用 Laravel 并且 laravel 使用 flysystem 进行 S3 管理,所以我没有简单的方法来获取 S3Client。因此我AWS SDK for Laravel在我的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}");
    }
}

我的第二个问题是 Do I need to Disable output buffering在 laravel 中?

恕我直言,答案是肯定的。缓冲让数据立即从缓冲区中刷新,从而降低内存消耗。由于我们没有使用任何 laravel 函数将数据卸载到客户端,这不是 laravel 完成的,因此需要由我们完成。

于 2015-07-28T23:32:54.947 回答