使用send_file从Amazon S3下载文件?

Dav*_*ite 56 ruby ruby-on-rails amazon-s3 ruby-on-rails-3 ruby-on-rails-3.2

我的应用程序中有一个下载链接,用户应该能够下载存储在s3上的文件.这些文件可以在网址上公开访问,看起来像

https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png
Run Code Online (Sandbox Code Playgroud)

下载链接在我的控制器中执行操作:

class AttachmentsController < ApplicationController
  def show
    @attachment = Attachment.find(params[:id])
    send_file(@attachment.file.url, disposition: 'attachment')
  end
end
Run Code Online (Sandbox Code Playgroud)

但是当我尝试下载文件时出现以下错误:

ActionController::MissingFile in AttachmentsController#show

Cannot read file https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png
Rails.root: /Users/user/dev/rails/print

Application Trace | Framework Trace | Full Trace
app/controllers/attachments_controller.rb:9:in `show'
Run Code Online (Sandbox Code Playgroud)

该文件肯定存在,可以在错误消息的URL中公开访问.

如何允许用户下载S3文件?

nza*_*ajt 82

你也可以使用send_data.

我喜欢这个选项,因为你有更好的控制力.您没有向s3发送用户,这可能会让某些用户感到困惑.

我只想添加一个下载方法 AttachmentsController

def download
  data = open("https://s3.amazonaws.com/PATTH TO YOUR FILE") 
  send_data data.read, filename: "NAME YOU WANT.pdf", type: "application/pdf", disposition: 'inline', stream: 'true', buffer_size: '4096' 
end 
Run Code Online (Sandbox Code Playgroud)

并添加路线

get "attachments/download"
Run Code Online (Sandbox Code Playgroud)

  • 那么在这个解决方案中,`open`方法不会先下载整个文件?send_data可以将文件从amazon流式传输给用户而无需用户知道真正的s3文件路径吗? (4认同)
  • 对于大文件,对于等待服务器首先下载文件然后将其流式传输给用户的用户来说,这可能会造成混淆.此外,只需要用户直接从S3下载,就需要两倍的时间. (3认同)
  • 值得指出的是,除了@JoshPinter观察到它变慢之外(因为数据通过中介而不是直接传递),它还给服务器增加了额外的负载并正在阻塞,尽管您可以将其卸载到后台任务。直接从S3下载文件效率更高,而且S3的错误页面也不错-但[访问私有文件时,您的AWS凭证将可见](http://stackoverflow.com/questions/12277971/using-send-file-to-从amazon-s3#comment63642384_12278021下载文件)。 (3认同)
  • 有了一个carrierwave + S3文件,我就这样工作:```article = Article.find params [:id]``````file_data = open(article.file.url)`````send_data file_data.read,filename:article.filename,type:article.file.content_type,disposition:'attachment'``` (2认同)

小智 30

为了从您的Web服务器发送文件,

  • 你需要从S3下载它(参见@ nzajt的答案)或

  • 您可以 redirect_to @attachment.file.expiring_url(10)

  • 如何在S3上使用非公共文件? (3认同)
  • 注意访问S3上的私有文件时如[此处]所示(http://stackoverflow.com/questions/9138994/awss3s3object-url-for-how-to-do-this-with-the-new-aws-sdk-gem ),S3 URL将包含您的秘密AWS凭证,以便浏览器可以进行经过身份验证的请求.如果下载工作且用户流停留在您的页面上并且不起作用(例如文件不存在),那么S3上的错误页面将在URL上显示凭据.这是一个相当大的安全风险. (2认同)

Jos*_*ter 30

让用户保持简单

我认为处理此问题的最佳方法是使用过期的S3网址.其他方法有以下问题:

  • 该文件首先下载到服务器,然后下载到用户.
  • 使用send_data不会产生预期的"浏览器下载".
  • 绑定Ruby进程.
  • 需要额外的download控制器操作.

我的实现看起来像这样:

在你的 attachment.rb

def download_url
  S3 = AWS::S3.new.buckets[ 'bucket_name' ] # This can be done elsewhere as well,
                                            # e.g config/environments/development.rb
  url_options = { 
    expires_in:                   60.minutes, 
    use_ssl:                      true, 
    response_content_disposition: "attachment; filename=\"#{attachment_file_name}\""
  }

  S3.objects[ self.path ].url_for( :read, url_options ).to_s
end
Run Code Online (Sandbox Code Playgroud)

在你的意见

<%= link_to 'Download Avicii by Avicii', attachment.download_url %>
Run Code Online (Sandbox Code Playgroud)

而已.


如果你仍然想download出于某种原因继续你的行动,那么就这样使用:

在你的 attachments_controller.rb

def download
  redirect_to @attachment.download_url
end
Run Code Online (Sandbox Code Playgroud)

感谢guilleva的指导.


Dav*_*row 5

以下是最终对我有用的内容。从 S3 对象获取原始数据,然后将send_data其传递给浏览器。

使用aws-sdk此处找到的gem 文档http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html

全控制器方法

def download
  AWS.config({
    access_key_id: "SECRET_KEY",
    secret_access_key: "SECRET_ACCESS_KEY"
  })

  send_data( 
    AWS::S3.new.buckets["S3_BUCKET"].objects["FILENAME"].read, {
      filename: "NAME_YOUR_FILE.pdf", 
      type: "application/pdf", 
      disposition: 'attachment', 
      stream: 'true', 
      buffer_size: '4096'
    }
  )
end
Run Code Online (Sandbox Code Playgroud)

  • 这必须在发送给用户之前将文件下载到服务器,对吗? (4认同)

小智 5

我刚刚将我的public/system文件夹迁移到了Amazon S3.上述解决方案有所帮助,但我的应用程序接受不同类型 所以如果你需要相同的行为,这对我有帮助:

@document = DriveDocument.where(id: params[:id])
if @document.present?
  @document.track_downloads(current_user) if current_user
  data = open(@document.attachment.expiring_url)
  send_data data.read, filename: @document.attachment_file_name, type: @document.attachment_content_type, disposition: 'attachment'
end
Run Code Online (Sandbox Code Playgroud)

该文件正保存在对象attachment字段中DriveDocument.我希望这有帮助.