首页 文章

使用send_file从Amazon S3下载文件?

提问于
浏览
55

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

https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png

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

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

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

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'

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

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

5 回答

  • 28

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

    • 您需要从S3下载(参见@nzajt's answer)或

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

  • 4

    你也可以使用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
    

    并添加路线

    get "attachments/download"
    
  • 5

    为用户保持简单

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

    • 文件首先下载到服务器,然后下载到用户 .

    • 使用 send_data 不会产生预期的"browser download" .

    • 绑定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
    

    在您的观点中

    <%= link_to 'Download Avicii by Avicii', attachment.download_url %>
    

    而已 .


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

    In your attachments_controller.rb

    def download
      redirect_to @attachment.download_url
    end
    

    感谢guilleva的指导 .

  • 29

    我刚刚将 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
    

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

  • 79

    以下是最终适合我的方式 . 从S3对象获取原始数据,然后使用 send_data 将其传递给浏览器 .

    使用 aws-sdk gem文档在这里找到http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html

    full controller method

    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
    

相关问题