Usando send_file per scaricare un file da Amazon S3?

Ho un link per il download nella mia app da cui gli utenti dovrebbero essere in grado di scaricare i file che sono memorizzati su s3. Questi file saranno accessibili pubblicamente su URL che assomigliano a qualcosa

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

Il link per il download colpisce un’azione nel mio controller:

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

Ma ottengo il seguente errore quando provo a scaricare un file:

 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' 

Il file esiste sicuramente ed è pubblicamente accessibile all’URL nel messaggio di errore.

Come posso consentire agli utenti di scaricare i file S3?

Per inviare un file dal tuo server web,

  • devi scaricarlo da S3 (vedi la risposta di @ nzajt ) o

  • puoi redirect_to @attachment.file.expiring_url(10)

Puoi anche usare send_data .

Mi piace questa opzione perché hai un controllo migliore. Non stai inviando utenti a s3, il che potrebbe essere fonte di confusione per alcuni utenti.

Vorrei solo aggiungere un metodo di download ad 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 aggiungi il percorso

 get "attachments/download" 

Mantieni le cose semplici per l’utente

Penso che il modo migliore per gestire questo è l’utilizzo di un URL S3 in scadenza. Gli altri metodi hanno i seguenti problemi:

  • Il file viene scaricato prima sul server e poi sull’utente.
  • L’utilizzo di send_data non produce il previsto “download del browser”.
  • Lega il processo Ruby.
  • Richiede un’azione di controller di download aggiuntiva.

La mia implementazione è la seguente:

Nel tuo 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 

Nelle tue opinioni

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

Questo è tutto.


Se vuoi comunque mantenere l’azione di download per qualche motivo, usa questo:

Nel tuo attachments_controller.rb

 def download redirect_to @attachment.download_url end 

Grazie a Guilleva per la sua guida.

Ho appena migrato la mia cartella public/system in Amazon S3. Le soluzioni sopra aiutano ma la mia app accetta diversi tipi di documenti. Quindi se hai bisogno dello stesso comportamento, questo mi aiuta:

 @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 

Il file viene salvato nel campo attachment dell’object DriveDocument . Spero che aiuti.

Quello che segue è quello che ha funzionato bene per me. Ottenere i dati grezzi dall’object S3 e quindi utilizzare send_data per passarli sul browser.

Usando la documentazione della gem aws-sdk trovata qui http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html

metodo di controllo completo

 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