He hecho esto en un sitio donde la gente paga para descargar ciertos archivos, y los archivos están almacenados en RAILS_ROOT/private
. Lo primero que debe saber es que desea que el servidor web se encargue del envío del archivo; de lo contrario, su aplicación se retrasará al transmitir archivos de gran tamaño, lo que hará que su sitio se detenga rápidamente si tiene algún tipo de volumen de descarga. Entonces, si necesita verificar la autorización en un controlador, también necesita una forma de pasar el control de la descarga al servidor web. La mejor manera de hacerlo (que yo sepa) es el encabezado X-Sendfile, que es compatible con Nginx, Apache (con módulo) y otros. Con X-Sendfile configurado, cuando su servidor web recibe un encabezado desde su aplicación, se hace cargo de enviar el archivo al cliente.
Una vez que tenga X-Sendfile de trabajo para su servidor web, un método de control privado de este tipo es útil:
##
# Send a protected file using the web server (via the x-sendfile header).
# Takes the absolute file system path to the file and, optionally, a MIME type.
#
def send_file(filepath, options = {})
options[:content_type] ||= "application/force-download"
response.headers['Content-Type'] = options[:content_type]
response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filepath)}\""
response.headers['X-Sendfile'] = filepath
response.headers['Content-length'] = File.size(filepath)
render :nothing => true
end
A continuación, el controlador de la acción podría ser algo como esto:
##
# Private file download: check permission first.
#
def download
product = Product.find_by_filename!(params[:filename])
if current_user.has_bought?(product) or current_user.is_superuser?
if File.exist?(path = product.filepath)
send_file path, :content_type => "application/pdf"
else
not_found
end
else
not_authorized
end
end
Obviamente Su método de autorización variará y deberá cambiar los encabezados si está ofreciendo archivos que no sean archivos PDF o si desea que el archivo se visualice en el navegador (elimine el tipo de contenido application/force-download
).
¿Por qué no está utilizando el método integrado send_file? –
¡Porque no lo sabía! Cualquiera que use esta técnica debería eliminar mi definición de método 'send_file' paralizado y, en su lugar, invocar la ruta' send_file' incluida en Rails,: type => "application/pdf",: x_sendfile => true'. Gracias por la nota, Ryan. –
He comentado más sobre 'X-Sendfile' en: http://stackoverflow.com/a/26884350/895245 –