Ruby on Rails, obligando al usuario a descargar un archivo TMP
-
28-10-2019 - |
Pregunta
He creado un archivo en el directorio TMP con el siguiente código del controlador:
def download
file_path = "#{RAILS_ROOT}/tmp/downloads/xxx.html"
data = render_to_string( :action => :show, :layout => nil )
File.open(file_path, "w"){|f| f << data }
flash[:notice] = "saved to #{file_path}"
end
Esto crea el archivo que quería en el directorio TMP, lo que quiero hacer es obligar al usuario a descargar ese archivo.
En mi máquina local, el archivo se guarda en ruta como:
/Users/xxxx/Documents/Sites/xxxx/Website/htdocs/tmp/downloads/xxxx.html
Y en el servidor en vivo, esta URL será algo totalmente diferente.
Lo que me preguntaba es cómo obligar al usuario a descargar este xxxx.html.
PD: si pongo un ...
redirect_to file_path
... En el controlador, solo me da una ruta que no se encuentra.
Salud.
Solución
Echa un vistazo al enviar archivo método. Se vería algo así:
send_file Rails.root.join('tmp', 'downloads', 'xxxxx.html'), :type => 'text/html', :disposition => 'attachment'
: disposition => 'Archivo' obligará al navegador a descargar el archivo en lugar de representarlo. Configure en 'en línea' si desea que se cargue en el navegador. Si NGINX está frente a su aplicación Rails, deberá modificar su configuración de entorno (es decir, entornos/producción.rb):
# For nginx:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
Otros consejos
Es fácil confundir rutas de archivos con URL, pero es una distinción importante. ¿Qué tiene un camino de URL de /a/b.txt
en realidad está ubicado en la ruta del sistema #{Rails.root}/public/a/b.txt
Por lo tanto, es posible que deba abordar esto generando ambos en conjunto.
Así es como puede abordar eso:
def download
base_path = "downloads/xxx.html"
system_path = File.expand_path("public/#{base_path}", Rails.root)
url_path = "/#{base_path}"
File.open(file_path, "w") do |f|
f.puts render_to_string(:action => :show, :layout => nil)
end
flash[:notice] = "saved to #{base_path}"
redirect_to(url_path)
end
No puede redirigir a un recurso que no esté expuesto a través de su servidor web, y generalmente solo cosas en public/
están establecidos de esta manera. Puede incluir rutas adicionales si configura su servidor en consecuencia.
También puede pasar todo este proceso todo simplemente renderizando la respuesta como un archivo adjunto en línea descargable, si lo prefiere:
render(:action => :show, :layout => nil, :content_type=> 'application/octet-stream')