Hacer 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 una vista para mostrarlo?

Author: damon, 2009-07-21

14 answers

Para lo mejor de ambos mundos, podría combinar la solución de S. Lott con el módulo xsendfile: django genera la ruta al archivo (o al archivo en sí), pero el servidor de archivos real es manejado por Apache/Lighttpd. Una vez que haya configurado mod_xsendfile, la integración con su vista requiere unas pocas 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 tiene control sobre su servidor, o si su empresa de alojamiento ya tiene mod_xsendfile configurado hasta.

EDITAR:

El tipo mime se sustituye por content_type para django 1.7

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

EDITAR: Para nginxmarque este, usa X-Accel-Redirect en lugar de apache el encabezado X-Sendfile.

 170
Author: elo80ka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-15 12:19:55

Una "descarga" es simplemente un cambio de encabezado HTTP.

Véase 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 de la solicitud 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 tener menos de 12 líneas de código.

 79
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-20 23:20:15

S. Lott tiene la solución "buena"/simple, y elo80ka tiene la solución "mejor"/eficiente. Aquí hay una solución"mejor" /media - sin configuración de servidor, pero más eficiente para archivos grandes que la solución ingenua:

Http://djangosnippets.org/snippets/365 /

Básicamente, Django todavía se encarga de servir el archivo, pero no carga todo en la memoria a la vez. Esto permite que su servidor sirva (lentamente) un archivo grande sin aumentar el uso de memoria.

De nuevo, S. Lott X-SendFile es aún mejor para archivos más grandes. Pero si no puede o no quiere molestarse con eso, entonces esta solución intermedia le ganará una mejor eficiencia sin la molestia.

 26
Author: rocketmonkeys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 22:31:20

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 ha mencionado 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))
 25
Author: Cory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-18 22:54:28

Probamos la solución @Rocketmonkeys pero los archivos descargados se almacenaban como *.bin y dado nombres aleatorios. Eso no está bien, por supuesto. Agregar otra línea de @elo80ka resolvió 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 dentro de /media ni /public_html) y exponerlos a través de django a ciertos usuarios o bajo ciertas circunstancias.
Espero que ayude.

Gracias a @elo80ka, @S. Lott y @Rocketmonkeys por el respuestas, consiguió la solución perfecta combinando todas ellas =)

 13
Author: Salvatorelab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-13 23:41:14

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á enviar cualquier archivo, siempre y cuando el nombre esté codificado en url, y el encabezado adicional:

X-SendFile-Encoding: url

También Se envía.

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

 12
Author: btimby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-14 17:10:18

Simplemente mencionando el objeto FileResponse disponible en Django 1.10

Editar: Me encontré 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 el futuro). Asume que el nombre de FileField es imported_file

views.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<filename>[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
 10
Author: shadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-15 15:18:53

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

"Abstracción para descargar las cargas de archivos al servidor web (por ejemplo, Apache con mod_xsendfile) una vez que Django ha comprobado los permisos, etc."

 6
Author: Roberto Rosario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-22 08:47:36

Debe usar las api de sendfile dadas por servidores populares como apache o nginx en producción. Muchos años estuve usando sendfile api de estos servidores para proteger archivos. Luego creó una simple aplicación django basada en middleware para este propósito, adecuada tanto para fines de desarrollo como de producción.Puede acceder al código fuente aquí.
ACTUALIZACIÓN: en la nueva versión python el proveedor usa 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
  • añadir fileprovider aplicación a INSTALLED_APPS ajustes,
  • añadir fileprovider.middleware.FileProviderMiddleware a MIDDLEWARE_CLASSES ajustes
  • establece la configuración de FILEPROVIDER_NAME en nginx o apache en producción, por defecto es python para fines de desarrollo.

En sus vistas basadas en clases o funciones, establezca el valor del encabezado de respuesta X-File como 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 impemented de una manera que su código necesitará solo mínimo modificación.

Configuración de 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 url de ubicación /files/ solo acceso interno, si está utilizando la configuración anterior puede establecer 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 django views

 5
Author: I Am Batman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-13 16:08:29

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 sencillo de configurar. Obstante. si es algún archivo.txt ' se genera a petición (el contenido es dinámico), entonces es posible que desee que django lo sirva.

Django Docs-Archivos estáticos

 1
Author: kjfletch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-21 09:49:56
def qrcodesave(request): 
    import urllib2;   
    url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0"; 
    opener = urllib2.urlopen(url);  
    content_type = "application/octet-stream"
    response = HttpResponse(opener.read(), content_type=content_type)
    response["Content-Disposition"]= "attachment; filename=aktel.png"
    return response 
 1
Author: Saurabh Chandra Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-21 10:12:41

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

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)
 0
Author: thyagx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-13 06:18:31

He enfrentado el mismo problema más de una vez y así implementado usando el módulo xsendfile y los decoradores de vista auth el django-filelibrary. Siéntase libre de usarlo como inspiración para su propia solución.

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

 0
Author: Daniel Sokolowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-01 22:07:51

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

 0
Author: iutinvg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-01 04:35:39