2012-09-05 14 views
26

Ho un'applicazione Rails ospitata su Heroku. L'app genera e archivia file PDF su Amazon S3. Gli utenti possono scaricare questi file per la visualizzazione nel proprio browser o per salvarli sul proprio computer.Rails: consente il download di file memorizzati su S3 senza mostrare l'effettivo URL S3 all'utente

Il problema che sto avendo è che sebbene il download di questi file sia possibile tramite l'URL S3 (come "https://s3.amazonaws.com/my-bucket/F4D8CESSDF.pdf"), ovviamente NON è un buon modo per farlo Non è opportuno esporre all'utente molte informazioni sul back-end, per non parlare dei problemi di sicurezza che si presentano.

È possibile che la mia app recuperi in qualche modo i dati dei file da S3 in un controller, quindi creare un flusso di download per l'utente, in modo che l'URL di Amazon non sia esposto?

+0

È ovviamente possibile scaricarlo e poi streaming indietro per l'utente, ma non sarà particolarmente veloce. –

risposta

9

Sì, questo è possibile: è sufficiente recuperare il file remoto con Rails e memorizzarlo temporaneamente sul server o inviarlo direttamente dal buffer. Il problema con questo è ovviamente il fatto che è necessario recuperare il file prima di poterlo servire all'utente. Vedere this thread per una discussione, la loro soluzione è qualcosa di simile:

#environment.rb 
require 'open-uri' 

#controller 
def index 
    data = open(params[:file]) 
    send_data data, :filename => params[:name], ... 
end 

This issue is also somewhat related.

+0

Questo si è rivelato essere semplice, grazie. Questa è la mia implementazione effettiva: download def file_name = params ["id_file"] + ".pdf" data = open ("https://s3.amazonaws.com/#{ENV['S3_BUCKET_NAME']}/ # {file_name} ") send_data data.read,: filename => file_name,: type =>" application/pdf ",: disposition => 'attachment', : stream => 'true',: buffer_size => ' 4096 ' fine Ho accettato la soluzione come soluzione a questo problema perché è la più semplice da implementare. – futureshocked

+0

Anche quello di Serge è buono, con il vantaggio di rilasciare l'app Rails dalla gestione del download, ha solo bisogno di un po 'più di lavoro per farlo funzionare. – futureshocked

+0

Solo altri sanno, ci sono due problemi con questo ho trovato: 1) Si scarica sul server e poi all'utente, che può richiedere un po '; e 2) L'esperienza per l'utente non è quello che si aspettano. Invece di ottenere un download nel browser, sembra invece che si stia caricando qualcosa. Controlla http://stackoverflow.com/a/23161355/293280 per un'ulteriore discussione. –

51

È possibile creare gli oggetti s3 come privati ​​e generare URL pubblici temporanei per loro con il metodo url_for (gemma aws-s3). In questo modo non si esegue lo streaming dei file attraverso i server delle app, che è più scalabile. Permette anche di mettere l'autorizzazione basata sulla sessione (ad esempio, ideare nell'app), tenere traccia degli eventi di download, ecc.

Per fare ciò, modificare i collegamenti diretti ai file ospitati s3 in collegamenti a controller/azione che crea l'URL temporaneo e reindirizza ad esso. In questo modo:

class HostedFilesController < ApplicationController 

    def show 
    s3_name = params[:id] # sanitize name here, restrict access to only some paths, etc 
    AWS::S3::Base.establish_connection!(...) 
    url = AWS::S3::S3Object.url_for(s3_name, YOUR_BUCKET, :expires_in => 2.minutes) 
    redirect_to url 
    end 

end 

Nascondere il dominio di Amazon negli url di download viene in genere eseguito con l'alias DNS. È necessario create CNAME record aliasing your subdomain, ad es. downloads.mydomain, a s3.amazonaws.com. Quindi è possibile specificare l'opzione :server in AWS::S3::Base.establish_connection!(:server => "downloads.mydomain", ...) e S3 gem lo utilizzerà per generare collegamenti.

+0

Serge, ho provato il tuo metodo. Funziona, tuttavia l'URL è simile a questo: http://s3.amazonaws.com/my-bucket/F4D8CED0.pdf?AWSAccessKeyId=my-actual-key&Expires=1346904098&Signature=Wo7wYH6Ek%2FBot3wd2xYbGt4ybSU3D. Quello che mi piacerebbe vedere idealmente non è menzione di Amazon, ma qualcosa come "http://mydomain.com/download/file.pdf. È possibile? – futureshocked

+0

Puoi avvicinarti molto a quello con CNAME, ma sarà più come 'something.mydomain.com/path/file.pdf'. Ho aggiornato la risposta per riflettere questo –

+0

Molto bella soluzione, grazie Serge. – futureshocked

-1

Per prima cosa è necessario creare un CNAME nel dominio, come spiegato here.

In secondo luogo è necessario creare un bucket con lo stesso nome inserito in CNAME.

E per finire è necessario aggiungere questo configurazioni in config/inizializzatori/carrierwave.rb:

CarrierWave.configure do |config| 
    ... 
    config.asset_host   = 'http://bucket_name.your_domain.com' 
    config.fog_directory  = 'bucket_name.your_domain.com' 
    ... 
end 
Problemi correlati