Usando send_file para baixar um arquivo do Amazon S3?

Eu tenho um link de download no meu aplicativo a partir do qual os usuários devem ser capazes de baixar arquivos que são armazenados no s3. Esses arquivos serão publicamente acessíveis em URLs que se parecem com

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

O link de download atinge uma ação no meu controlador:

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

Mas eu recebo o seguinte erro ao tentar baixar um arquivo:

 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' 

O arquivo definitivamente existe e é publicamente acessível na URL na mensagem de erro.

Como eu permito que os usuários baixem arquivos S3?

Para enviar um arquivo do seu servidor da Web,

  • você precisa baixá-lo do S3 (veja a resposta do @ nzajt ) ou

  • você pode redirect_to @attachment.file.expiring_url(10)

Você também pode usar send_data .

Eu gosto desta opção porque você tem um melhor controle. Você não está enviando usuários para s3, o que pode ser confuso para alguns usuários.

Gostaria apenas de adicionar um método de download ao 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 

e adicione a rota

 get "attachments/download" 

Mantenha as coisas simples para o usuário

Acho que a melhor maneira de lidar com isso é usar um URL S3 expirado. Os outros methods têm os seguintes problemas:

  • O arquivo é baixado primeiro para o servidor e depois para o usuário.
  • Usar send_data não produz o “download do navegador” esperado.
  • Ative o processo Ruby.
  • Requer uma ação adicional do controlador de download .

Minha implementação é assim:

Em seu attachment.rb

 def download_url S3 = AWS::S3.new.buckets[ 'bucket_name' ] # This can be done elsewhere as well, # eg 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 

Em seus pontos de vista

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

É isso aí.


Se você ainda quiser manter sua ação de download por algum motivo, basta usar isto:

Em seu attachments_controller.rb

 def download redirect_to @attachment.download_url end 

Graças a guilleva por sua orientação.

Acabei de migrar minha pasta public/system para o Amazon S3. As soluções acima ajudam, mas meu aplicativo aceita diferentes tipos de documentos. Então, se você precisa do mesmo comportamento, isso me ajuda:

 @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 

O arquivo está sendo salvo no campo de attachment do object DriveDocument . Eu espero que isso ajude.

O seguinte é o que acabou funcionando bem para mim. Obtendo os dados brutos do object S3 e, em seguida, usando send_data para passar isso para o navegador.

Usando a documentação do aws-sdk gem encontrada aqui http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html

método controlador total

 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