在Rails上从S3提供大文件

时间:2016-01-19 19:54:16

标签: ruby-on-rails heroku amazon-s3

我需要能够通过Rails应用程序从S3存储桶中下载大文件。我通常只会向用户提供3S存储桶的URL,但我需要它保持屏蔽状态,以便我可以随意过期或删除URL。我做了类似以下的事情:

def download
  purchase = Purchase.find(params[:id])

  data = open(purchase[:download_url])
  send_data data.read, filename: purchase[:file_name]
end

它适用于较小的文件,但是在1GB上,比如1GB,由于Heroku的超时限制,用户会收到大量的520错误。

我理解这里发生了什么:整个文件在被发送给用户之前在我的应用程序中打开,这就是为什么大文件超时的原因。我想知道的是,如果没有给用户S3 URL,是否有办法解决这个问题?是否可以,而不是下载文件,只是掩盖URL(而不是重定向)?

感谢任何帮助!

修改

根据SO的其他答案,我尝试实施过期网址:

def download
  purchase = Purchase.find(params[:id])
  path = purchase[:download_url].sub! 'entire URL until bucket path', ''
  s3URL = AWS::S3::S3Object.new(S3_BUCKET, path)

  redirect_to s3URL.url_for(:read).to_s
end

这只是重定向到AWS上的AccessDenied,但我可能错误地构建了URL。再次,任何帮助表示赞赏!

2 个答案:

答案 0 :(得分:2)

我认为您的S3对象网址无法访问,因为存储桶/对象没有public-read

要允许人们直接从S3存储桶下载,您可以:

  • 让您的广告可以访问,或
  • 创建一个预先签名的网址,以授权匿名S3用户下载该文件。

使S3存储桶可公开访问

如果您使用此方法,则可以像更新的问题一样提供S3对象网址。您只需在AWS中配置存储桶。

  1. 转到S3控制台。
  2. 打开您的存储桶属性(右键单击您的存储桶,然后单击属性)。
  3. 单击权限部分,然后单击添加存储桶策略
  4. 复制并粘贴以下存储桶策略:

    {
      "Version":"2012-10-17",
      "Statement":[
        {
          "Sid":"AddPerm",
          "Effect":"Allow",
          "Principal": "*",
          "Action":["s3:GetObject"],
          "Resource":["arn:aws:s3:::examplebucket/*"]
        }
      ]
    }
    

    来源:http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html#example-bucket-policies-use-case-2

  5. 点击保存。现在,任何用户都可以下载此存储桶中的任何对象,只要他们有URL即可。作为提示,要禁止猜测URL,您可以使用合理长度的随机字符串重命名对象密钥。

  6. 创建预先签名的网址

    在此方法中,只有拥有预分配网址的用户才能在特定时间段内下载您的S3对象(默认情况下,它为一周)。在那之后,他/她需要一个新的。要创建预签名的S3对象:

    def download
      purchase = Purchase.find(params[:id])
      path = purchase[:download_url].sub! 'entire URL until bucket path', ''
    
      s3 = Aws::S3::Resource.new(region:'us-west-2')
      object = s3.bucket(S3_BUCKET).object(path)
      redirect_to object.presigned_url(:get)
    end
    

    您也可以在object.presigned_url方法中设置一些选项,例如:

    object.presigned_url(:get, expires_in: 3600, response_content_disposition: 'attachment; filename=original_file_name.zip')
    

    http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method中阅读更多内容。

    您需要选择哪一种适合您的情况。对于非敏感和公共S3对象,我更喜欢将S3存储桶公共访问。但是,我认为,对于您的情况(购买东西然后下载),最好实现预先签名的URL。

答案 1 :(得分:0)

您不需要发送数据,只需设置代理网址,重定向到S3网址,或者您需要以编程方式执行的任何其他操作。

另请注意,S3网址已过期。