2009-08-24 9 views
11

En mi aplicación, tengo un requisito que me está superando.Usando Send_File a un origen remoto (Ruby on Rails)

Tengo un archivo almacenado en S3, y cuando un usuario hace clic en un enlace en mi aplicación, inicio sesión en la base de datos que hicieron clic en el enlace, disminuyo su asignación de 'crédito de descarga' en una y luego quiero solicita el archivo para descargar.

simplemente no quieren redirigir al usuario al archivo porque está almacenado en S3 y yo no quiero que tengan el enlace del archivo de origen (para que pueda mantener la integridad y el acceso)

Parece que send_file() no funciona con un archivo fuente remoto, ¿alguien recomienda una gema o un código adecuado que hará esto?

+0

posible duplicado de [cómo forzar el envío \ _data para descargar el archivo en el navegador?] (Http://stackoverflow.com/questions/7337508/how-to-force-send-data-to-download- the-file-in-the-browser) –

Respuesta

9

Necesitará transmitir el contenido del archivo al usuario mientras lo lee desde el cubo/objeto S3.

Si utiliza el AWS::S3 biblioteca de algo como esto puede funcionar:

send_file_headers!(:length=>S3Object.about(<s3 object>, <s3 bucket>)["content-length"], :filename=><the filename>) 
render :status => 200, :text => Proc.new { |response, output| 
    S3Object.stream(<s3 object>, <s3 bucket>) do |chunk| 
    output.write chunk 
    end 
} 

Este código es copiado en su mayoría forma el código send_file que por sí sólo funciona para archivos locales o de objetos de archivo similar a

N. B. De todos modos, aconsejaría no servir el archivo del proceso de los rieles. Si es posible/aceptable para su caso de uso, utilizaría un autenticado GET para mostrar los datos privados del depósito.

Al utilizar un GET autenticado, puede mantener el depósito y sus objetos en privado, al tiempo que permite el permiso temporal para leer un contenido de objeto específico mediante la creación de una URL que incluye un token de firma de autenticación. El usuario simplemente se redirige a la URL autenticada y el token puede hacerse válido por solo unos minutos.

Uso de la mencionada AWS :: S3 se puede obtener una URL autenticado GET de esta manera:

time_of_exipry = Time.now + 2.minutes 
S3Object.url_for(<s3 object>, <s3 bucket>, 
        :expires => time_of_exipry) 
+0

el S3Ojbect.url_for parece el mejor enfoque – klochner

+2

Utilizamos un redireccionamiento durante un año, pero ahora realmente queremos que Safari/Mac funcione: http://stackoverflow.com/questions/1995589/html5-audio-safari-live-broadcast-vs-not send_file_headers!es privado, ni send_data ni send_file acepta archivos remotos; workaroundable but just FYI – kain

+0

Creo que la forma de generar time_of_expiry en realidad no funcionará, debe hacer: 'time_of_expiry = 2.minutes.from_now.to_i' – Raphael

3

Usted puede leer el archivo desde S3 y escribir de forma local en un directorio que no sea pública, a continuación, utilizar X-Sendfile (apache) o X-Accel-Redirect (nginx) para servir el contenido.

Para Nginx que incluiría algo así como lo siguiente en su fichero de configuración:


      location /private { 
           internal; 
           alias /path/to/private/directory/; 
      } 

Luego, en el controlador de rieles, de hacer lo siguiente:


    response.headers['Content-Type'] = your_content_type 
    response.headers['Content-Disposition'] = "attachment; filename=#{your_file_name}" 
    response.headers['Cache-Control'] = "private" 
    response.headers['X-Accel-Redirect'] = path_to_your_file 
    render :nothing=>true 

Una buena valoración crítica del proceso es here

+0

lo que usted dice es correcto, pero ¿habría alguna forma de hacerlo sin que los archivos sean locales para el servidor web? El problema en el caso que nos ocupa es que los archivos están almacenados en el servicio web S3. – LucaM

+1

No creo que pueda otorgar a las personas acceso directo por usuario a sus archivos S3 a menos que tengan su propia cuenta S3. Tendrás que descargar los archivos de S3 a tu servidor local y enviarlos desde allí. – klochner

+0

Los archivos s3 a menudo son públicos, solo usan URI ofuscados. Parece que el efecto que está tratando de lograr se puede hacer sin copiar los archivos, consulte la discusión aquí: http://www.ruby-forum.com/topic/132214. Sin embargo, tendrá que hacer 2 etapas: iniciar sesión y luego enviar la solicitud del archivo. – austinfromboston

3

Método de descarga de imagen completa usando el archivo temporal (raíles probados 3.2):

def download 
    @image = Image.find(params[:image_id]) 

    open(@image.url) {|img| 
    tmpfile = Tempfile.new("download.jpg") 
    File.open(tmpfile.path, 'wb') do |f| 
     f.write img.read 
    end 
    send_file tmpfile.path, :filename => "great-image.jpg" 
    } 
end 
+0

¡Funciona como un encanto! ¡Tempfile se borra automáticamente en GC! –