Que Django sirva archivos descargables.

Quiero que los usuarios del sitio puedan descargar archivos cuyas rutas estén ocultas para que no puedan descargarse directamente.

Por ejemplo, me gustaría que la URL fuera algo como esto “, http://example.com/download/?f=somefile.txt

Y en el servidor, sé que todos los archivos descargables residen en una carpeta “/ home / user / files /”.

¿Hay alguna manera de hacer que Django sirva ese archivo para descargar en lugar de intentar encontrar una URL y Ver para mostrarlo?

Para “lo mejor de los dos mundos”, puede combinar la solución de S.Lott con el módulo xsendfile : django genera la ruta al archivo (o el archivo en sí), pero Apache / Lighttpd maneja el archivo real. Una vez que haya configurado mod_xsendfile, la integración con su vista toma algunas líneas de código:

from django.utils.encoding import smart_str response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for django 1.7 response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name) response['X-Sendfile'] = smart_str(path_to_file) # It's usually a good idea to set the 'Content-Length' header too. # You can also set any other required headers: Cache-Control, etc. return response 

Por supuesto, esto solo funcionará si usted tiene control sobre su servidor, o si su compañía de alojamiento ya ha configurado mod_xsendfile.

EDITAR:

mimetype es reemplazado por content_type para django 1.7

 response = HttpResponse(content_type='application/force-download' 

EDITAR: Para nginx verifique esto , usa X-Accel-Redirect lugar del encabezado de apache X-Sendfile.

Una “descarga” es simplemente un cambio de encabezado HTTP.

Consulte http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment para saber cómo responder con una descarga .

Solo necesita una definición de URL para "/download" .

El diccionario GET o POST tendrá la información "f=somefile.txt" .

Su función de vista simplemente fusionará la ruta base con el valor ” f “, abrirá el archivo, creará y devolverá un objeto de respuesta. Debe ser menos de 12 líneas de código.

Para una solución muy simple pero no eficiente o escalable , solo puede usar la vista integrada de Django serve . Esto es excelente para prototipos rápidos o trabajos únicos, pero como se mencionó a lo largo de esta pregunta, debe usar algo como apache o nginx en producción.

 from django.views.static import serve filepath = '/some/path/to/local/file.txt' return serve(request, os.path.basename(filepath), os.path.dirname(filepath)) 

S.Lott tiene la solución “buena” / simple, y elo80ka tiene la solución “mejor” / eficiente. Aquí hay una solución “mejor” / intermedia: no hay configuración del servidor, pero es más eficiente para archivos grandes que la solución ingenua:

http://djangosnippets.org/snippets/365/

Básicamente, Django aún maneja el archivo, pero no carga todo en la memoria de una vez. Esto permite que su servidor sirva (lentamente) un archivo grande sin boost el uso de la memoria.

Nuevamente, el archivo X-SendFile de S.Lott sigue siendo mejor para archivos más grandes. Pero si no puedes o no quieres molestarte con eso, entonces esta solución intermedia te dará una mayor eficiencia sin la molestia.

Se probó la solución @Rocketmonkeys pero los archivos descargados se almacenaron como * .bin y se les dieron nombres aleatorios. Eso no está bien, por supuesto. Añadir otra línea de @ elo80ka solucionó el problema.
Aquí está el código que estoy usando ahora:

 from wsgiref.util import FileWrapper from django.http import HttpResponse filename = "/home/stackoverflow-addict/private-folder(not-porn)/image.jpg" wrapper = FileWrapper(file(filename)) response = HttpResponse(wrapper, content_type='text/plain') response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(filename) response['Content-Length'] = os.path.getsize(filename) return response 

Ahora puede almacenar archivos en un directorio privado (no en / media ni / public_html) y exponerlos a través de django a ciertos usuarios o bajo ciertas circunstancias.
Espero eso ayude.

Gracias a @ elo80ka, @ S.Lott y @Rocketmonkeys por las respuestas, obtuve la solución perfecta combinándolas todas =)

Se mencionó anteriormente que el método mod_xsendfile no permite caracteres no ASCII en los nombres de archivo.

Por esta razón, tengo un parche disponible para mod_xsendfile que permitirá que se envíe cualquier archivo, siempre que el nombre esté codificado como URL y el encabezado adicional:

 X-SendFile-Encoding: url 

Se envía también.

http://ben.timby.com/?p=149

Solo mencionando el objeto FileResponse disponible en Django 1.10

Edición: me topé con mi propia respuesta mientras buscaba una manera fácil de transmitir archivos a través de Django, así que aquí hay un ejemplo más completo (para mí en el futuro). Se asume que el nombre de FileField es imported_file

vistas.py

 from django.views.generic.detail import DetailView from django.http import FileResponse class BaseFileDownloadView(DetailView): def get(self, request, *args, **kwargs): filename=self.kwargs.get('filename', None) if filename is None: raise ValueError("Found empty filename") some_file = self.model.objects.get(imported_file=filename) response = FileResponse(some_file.imported_file, content_type="text/csv") # https://docs.djangoproject.com/en/1.11/howto/outputting-csv/#streaming-large-csv-files response['Content-Disposition'] = 'attachment; filename="%s"'%filename return response class SomeFileDownloadView(BaseFileDownloadView): model = SomeModel 

urls.py

 ... url(r'^somefile/(?P[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'), ... 

Pruebe: https://pypi.python.org/pypi/django-sendfile/

“Una vez que Django haya revisado los permisos, etc.” se realizó una abstracción para descargar archivos subidos al servidor web (por ejemplo, Apache con mod_xsendfile).

Debe utilizar las apis de sendfile dadas por servidores populares como apache o nginx en producción. Durante muchos años, estuve usando la aplicación sendfile de estos servidores para proteger archivos. Luego creó una aplicación django sencilla basada en middleware para este propósito, adecuada tanto para desarrollo como para producción. Aquí puede acceder al código fuente.
ACTUALIZACIÓN: en la nueva versión, el proveedor de python utiliza django FileResponse si está disponible y también agrega soporte para muchas implementaciones de servidor desde lighthttp, caddy a hiawatha

Uso

 pip install django-fileprovider 
  • agrega la aplicación fileprovider a la configuración de INSTALLED_APPS ,
  • agrega fileprovider.middleware.FileProviderMiddleware a la configuración de MIDDLEWARE_CLASSES
  • establezca la configuración de FILEPROVIDER_NAME en nginx o apache en producción, de forma predeterminada es python para fines de desarrollo.

en sus vistas basadas en clases o funciones, establezca el valor del X-File encabezado de respuesta en la ruta absoluta al archivo. Por ejemplo,

 def hello(request): // code to check or protect the file from unauthorized access response = HttpResponse() response['X-File'] = '/absolute/path/to/file' return response 

django-fileprovider incrementó de manera que su código solo necesitará una modificación mínima.

Configuracion nginx

Para proteger el archivo del acceso directo, puede establecer la configuración como

  location /files/ { internal; root /home/sideffect0/secret_files/; } 

Aquí nginx establece una ubicación url /files/ solo acceso interno, si está utilizando la configuración anterior, puede configurar X-File como,

 response['X-File'] = '/files/filename.extension' 

Al hacer esto con la configuración de nginx, el archivo estará protegido y también puede controlar el archivo desde las views django

Django recomienda que use otro servidor para servir medios estáticos (otro servidor que se ejecute en la misma máquina está bien). Recomiendan el uso de servidores como lighttp .

Esto es muy simple de configurar. Sin embargo. si se genera ‘somefile.txt’ a pedido (el contenido es dynamic), es posible que desee que django lo sirva.

Django Docs – Archivos Estáticos

 def qrcodesave(request): import urllib2; url ="http://sofes.miximages.com/download/octet-stream response = HttpResponse(opener.read(), content_type=content_type) response[Content-Disposition]= attachment; filename=aktel.png" return response 

Otro proyecto para echar un vistazo a: http://readthedocs.org/docs/django-private-files/en/latest/usage.html Parece prometedora, no lo he probado todavía.

Básicamente, el proyecto abstrae la configuración mod_xsendfile y le permite hacer cosas como:

 from django.db import models from django.contrib.auth.models import User from private_files import PrivateFileField def is_owner(request, instance): return (not request.user.is_anonymous()) and request.user.is_authenticated and instance.owner.pk = request.user.pk class FileSubmission(models.Model): description = models.CharField("description", max_length = 200) owner = models.ForeignKey(User) uploaded_file = PrivateFileField("file", upload_to = 'uploads', condition = is_owner) 

He enfrentado el mismo problema más de una vez y, por lo tanto, lo he implementado utilizando el módulo xsendfile y los decoradores de vistas de autenticación en la biblioteca de archivos Django . Siéntase libre de usarlo como inspiración para su propia solución.

https://github.com/danielsokolowski/django-filelibrary

Proporcionar acceso protegido a la carpeta html estática usando https://github.com/johnsensible/django-sendfile : https://gist.github.com/iutinvg/9907731