【问题标题】:Serving large files from S3 on Rails从 S3 on Rails 提供大文件
【发布时间】:2016-01-19 19:54:16
【问题描述】:

我需要能够从 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 的文件上,由于 Heroku 的超时限制,用户会收到一个很大的 520 错误。

我了解这里发生了什么:整个文件在发送给用户之前在我的应用程序中打开,这就是为什么对于较大的文件它会超时。我想知道是否有办法在不给用户 S3 URL 的情况下解决这个问题?是否有可能,而不是下载文件,只是屏蔽 URL(而不是重定向)?

感谢任何帮助!

编辑

基于 SO 上的其他答案,我尝试实现一个过期 URL:

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 不正确。再次感谢任何帮助!

【问题讨论】:

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


    【解决方案1】:

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

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

    • 让您的存储桶可公开访问,或
    • 创建一个预签名 URL 以授权匿名 S3 用户下载文件。

    公开 S3 存储桶

    如果您遵循此方法,您可以在更新后的问题中提供 S3 对象 url。您只需要在 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,您可以使用合理长度的随机字符串重命名对象键。

    创建一个预签名的网址

    在此方法中,只有拥有预签名 url 的用户才能在一定期限内(默认为一周)下载您的 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。

    【讨论】:

    • 嘿,爱德华,谢谢!我选择了你的第二个解决方案,除了我必须使用url_for,因为我使用的是 gem 的 v1。它会产生类似https://mybucket.s3.amazonaws.com/bucket_path_file.zip?AWSAccessKeyId=XXXXX&Expires=1453753227&Signature=XXXXX&response-content-disposition=attachment 的东西——我现在的问题是https://mybucket.s3.amazonaws.com/bucket_path_file.zip 仍然有效。我应该如何使用我的政策/权限来防止这种情况发生?
    • 是的,生成的 url 看起来像一个预签名的 url。你是如何上传你的对象的?您是否将 ACL 设置为 public-read
    • 是的,它是public-read。我在我的 Rails 控制器中创建对象数据,如下所示:S3_BUCKET.presigned_post(key: "#{SecureRandom.uuid}-${filename}", success_action_status: '201', acl: 'public-read'),然后将此信息传递给jQuery-File-Upload 以处理其余部分。 ACL 应该设置成什么?
    • 您可以删除acl 选项(即'private' acl)。 ACL 的完整列表可以在docs.aws.amazon.com/AmazonS3/latest/dev/… 中找到。
    • 太棒了。谢谢你!除了将 ACL 设置为 private 之外,我还必须将存储桶策略的主体更新为特定于 IAM 帐户而不是匿名的。你得到奖品!再次感谢。
    【解决方案2】:

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

    还请记住,S3 url 已过期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-30
      • 1970-01-01
      • 2015-03-05
      • 2018-06-06
      • 1970-01-01
      • 2017-10-15
      • 1970-01-01
      相关资源
      最近更新 更多