64

我的应用程序中有一个下载链接,用户应该可以从中下载存储在 s3 上的文件。这些文件将在看起来像这样的 url 上公开访问

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 文件?

4

7 回答 7

92

您也可以使用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"
于 2013-02-22T15:59:03.567 回答
37

为用户保持简单

我认为处理此问题的最佳方法是使用即将到期的 S3 url。其他方法存在以下问题:

  • 文件首先下载到服务器,然后再下载到用户。
  • 使用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

在你看来

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

而已。


download如果您出于某种原因仍想保留您的操作,请使用以下命令:

在你的attachments_controller.rb

def download
  redirect_to @attachment.download_url
end

谢吉列瓦的指导。

于 2014-04-18T19:58:38.233 回答
32

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

  • 您需要从 S3 下载它(请参阅@nzajt 的答案)或

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

于 2012-09-05T09:09:39.810 回答
6

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

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

于 2015-01-12T03:44:12.680 回答
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
于 2014-03-04T20:10:57.553 回答
0

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

如果您能够在将文件上传到 S3 之前在文件上设置一些元数据,而不是在用户稍后想要下载它时尝试修补它,那么这个解决方案会简单得多:

https://stackoverflow.com/a/24297799/763231

如果您正在使用,fog那么您可以执行以下操作:

has_attached_file :report,
  fog_file: lambda { |attachment|
    {
      content_type: 'text/csv',
      content_disposition: "attachment; filename=#{attachment.original_filename}",
    }
  }

如果您使用 Amazon S3 作为您的存储提供程序,那么这样的事情应该可以工作:

has_attached_file :report
  s3_headers: lambda { |attachment|
    { 
      'Content-Type' => 'text/csv',
      'Content-Disposition' => "attachment; filename=#{attachment.original_filename}",
    }
  }
于 2020-07-30T04:18:13.303 回答
-1

def download_pdf @post= @post.avatar.service_url

send_data(

    "#{Rails.root}/public/#{@post}",
    filename: "#{@post}",
    type: "image/*",
    disposition: 'inline', stream: 'true', buffer_size: '4096'
)

结尾

于 2020-06-12T11:36:11.710 回答