Que Django serve des fichiers téléchargeables

je veux que les utilisateurs sur le site soient en mesure de télécharger des fichiers dont les chemins sont obscurcis de sorte qu'ils ne peuvent pas être directement téléchargés.

par exemple, j'aimerais que l'URL soit quelque chose comme ça, " http://example.com/download/?f=somefile.txt

Et sur le serveur, je sais que tous les fichiers téléchargeables résident dans un dossier "/home/utilisateur/fichiers/".

est-il un moyen de faire Django servir ce fichier à télécharger comme opposé à essayer de trouver une URL et une vue pour l'afficher?

208
demandé sur damon 2009-07-21 02:10:30

14 réponses

pour le" meilleur des Deux Mondes", vous pouvez combiner la solution de S. Lott avec le module xsendfile module : django génère le chemin vers le fichier (ou le fichier lui-même), mais la portion de fichier actuelle est gérée par Apache/Lighttpd. Une fois que vous avez configuré mod_xsendfile, l'intégration à votre vue prend quelques lignes de code:

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

bien sûr, cela ne fonctionnera que si vous avez le contrôle de votre serveur, ou si votre hébergeur A mod_xsendfile déjà mis en place.

EDIT:

mimetype est remplacé par content_type pour django 1.7

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

EDIT: Pour nginx cochez ce , il utilise X-Accel-Redirect au lieu de apache en-tête X-Sendfile.

170
répondu elo80ka 2017-09-15 12:19:55

Un "téléchargement" est tout simplement un en-tête HTTP changement.

Voir http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment pour savoir comment réagir avec un téléchargement.

vous n'avez besoin que d'une définition D'URL pour "/download" .

le dictionnaire de la demande GET ou POST contiendra l'information "f=somefile.txt" .

votre fonction de vue va simplement fusionner le chemin de base avec la valeur " f ", ouvrir le fichier, créer et retourner un objet de réponse. Il doit y avoir moins de 12 lignes de code.

79
répondu S.Lott 2009-07-20 23:20:15

S. Lott a la solution "bonne" / simple, et elo80ka a la solution"meilleure" /efficace. Voici une solution"meilleure" /intermédiaire - pas de configuration de serveur, mais plus efficace pour les gros fichiers que le correctif naïf:

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

en gros, Django s'occupe toujours de servir le fichier mais ne charge pas tout en mémoire à la fois. Cela permet à votre serveur (lentement) servir d'un gros fichier sans rampe de l'utilisation de la mémoire.

encore une fois, le x-SendFile de S. Lott est encore meilleur pour les gros fichiers. Mais si vous ne pouvez pas ou ne voulez pas vous embêter avec cela, alors cette solution vous gagnera une meilleure efficacité, sans les tracas.

26
répondu rocketmonkeys 2017-03-21 22:31:20

pour une solution très simple mais pas efficace ou évolutive , vous pouvez simplement utiliser la vue construite en django serve . C'est excellent pour les prototypes rapides ou les travaux ponctuels, mais comme cela a été mentionné tout au long de cette question, vous devriez utiliser quelque chose comme apache ou nginx en production.

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
répondu Cory 2014-01-18 22:54:28

a essayé la solution de @Rocketmonkeys, mais les fichiers téléchargés étaient stockés en tant que *.bin et donné des noms aléatoires. C'est pas bien, bien sûr. L'ajout d'une autre ligne de @elo80ka a résolu le problème.

Voici le code que j'utilise maintenant:

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

vous pouvez désormais stocker des fichiers dans un répertoire privé (pas dans /media ni /public_html) et les exposer via django à certains utilisateurs ou dans certaines circonstances.

Espérons que cela aide.



merci à @elo80ka, @S. Lott et @Rocketmonkeys pour les réponses, obtenu la solution parfaite combinant tous les=)

13
répondu Salvatorelab 2018-07-13 23:41:14

il a été mentionné ci-dessus que la méthode mod_xsendfile ne permet pas les caractères non-ASCII dans les noms de fichiers.

Pour cette raison, j'ai un patch disponible pour mod_xsendfile qui permettra à n'importe quel fichier à envoyer, tant que le nom est encodée url, et l'en-tête supplémentaire:

X-SendFile-Encoding: url

est aussi envoyé.

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

12
répondu btimby 2011-03-14 17:10:18

le simple fait de mentionner le FileResponse objet disponible dans Django 1.10

Edit: je viens d'entrer dans ma propre réponse tout en cherchant un moyen facile de diffuser des fichiers via Django, donc voici un exemple plus complet (pour moi futur). Il suppose que le nom de FileField est 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
répondu shadi 2018-03-15 15:18:53

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

" Abstraction pour décharger les téléchargements de fichiers vers le serveur web (par exemple Apache avec mod_xsendfile) une fois que Django a vérifié les permissions, etc."

6
répondu Roberto Rosario 2016-01-22 08:47:36

vous devez utiliser les API sendfile données par les serveurs populaires comme apache ou nginx dans la production. Pendant de nombreuses années, j'ai utilisé l'api sendfile de ces serveurs pour protéger les fichiers. Puis a créé un middleware simple basé django app pour ce but adapté à la fois pour le développement et la production.Vous pouvez accéder au code source ici .

Mise à jour: dans la nouvelle version python le fournisseur utilise django FileResponse si disponible et ajoute également le soutien pour de nombreuses implémentations de serveur de lighthttp, caddy à hiawatha

Utilisation

pip install django-fileprovider
  • ajouter fileprovider app INSTALLED_APPS paramètres",
  • ajouter fileprovider.middleware.FileProviderMiddleware à MIDDLEWARE_CLASSES paramètres
  • set FILEPROVIDER_NAME paramètres nginx ou apache dans la production, par défaut c'est python pour des fins de développement.

dans l'en-tête de réponse X-File de votre classe ou de votre fonction de vue. Par exemple,

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 empémenté d'une manière qui votre code n'aura besoin que d'une modification minimale.

NGINX configuration

pour protéger le fichier de l'accès direct, vous pouvez définir la configuration comme

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

ici nginx définit une url de localisation /files/ accès interne seulement, si vous utilisez la configuration ci-dessus, vous pouvez définir X-File comme,

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

en faisant cela avec la configuration de nginx, le fichier sera protégé et aussi vous pouvez contrôler le fichier de django views

5
répondu I Am Batman 2017-08-13 16:08:29

Django vous recommande d'utiliser un autre serveur pour servir des médias statiques (un autre serveur tournant sur la même machine est parfait.) Ils recommandent l'utilisation de serveurs tels que lighttp .

C'est très simple à mettre en place. Cependant. si " unfichier.txt " est généré sur demande (contenu dynamique), alors vous pouvez django de le servir.

Django Docs - Fichiers Statiques

1
répondu kjfletch 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
répondu Saurabh Chandra Patel 2016-01-21 10:12:41

un autre projet à examiner: http://readthedocs.org/docs/django-private-files/en/latest/usage.html On dirait promissing, Je ne l'ai pas encore testé moi-même.

essentiellement le projet résume la configuration mod_xsendfile et vous permet de faire des choses comme:

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
répondu thyagx 2012-01-13 06:18:31

j'ai fait face au même problème plus d'une fois et ainsi mis en œuvre en utilisant le module xsendfile et les décorateurs de vue auth le django-filelibrary . N'hésitez pas à l'utiliser comme source d'inspiration pour votre propre solution.

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

0
répondu Daniel Sokolowski 2012-05-01 22:07:51

fournissant un accès protégé au dossier html statique en utilisant https://github.com/johnsensible/django-sendfile : https://gist.github.com/iutinvg/9907731

0
répondu iutinvg 2014-04-01 04:35:39