web-dev-qa-db-fra.com

Avoir Django servir des fichiers téléchargeables

Je souhaite que les utilisateurs du site puissent télécharger des fichiers dont le chemin est obscurci afin qu'ils ne puissent pas être téléchargés directement.

Par exemple, j'aimerais que l'URL ressemble à ceci: " 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/user/files /".

Existe-t-il un moyen de faire en sorte que Django serve ce fichier au téléchargement, au lieu d'essayer de trouver une URL et Afficher pour l'afficher?

228
damon

Pour le "meilleur des deux mondes", vous pouvez combiner la solution de S.Lott avec le module xsendfile : Django génère le chemin d'accès au fichier (ou au fichier lui-même). le service de fichiers est géré 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 entendu, cela ne fonctionnera que si vous avez le contrôle de votre serveur ou si votre hébergeur a déjà configuré mod_xsendfile.

EDIT:

mimetype est remplacé par content_type pour Django 1.7

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

EDIT: Pour nginx check this , il utilise X-Accel-Redirect au lieu de Apache En-tête X-Sendfile.

184
elo80ka

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

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épondre avec un téléchargement.

Vous n'avez besoin que d'une seule définition d'URL pour "/download".

Le dictionnaire GET ou POST de la demande contiendra les informations "f=somefile.txt".

Votre fonction de vue fusionnera simplement le chemin de base avec la valeur "f", ouvrira le fichier, créera et retournera un objet de réponse. Il devrait y avoir moins de 12 lignes de code.

85
S.Lott

Pour une solution très simple mais pas efficace ni évolutive, vous pouvez simplement utiliser la vue intégrée 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))
27
Cory

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

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

Fondamentalement, Django gère toujours le service du fichier mais ne charge pas le tout en mémoire en une fois. Cela permet à votre serveur de servir (lentement) un gros fichier sans augmenter l'utilisation de la mémoire.

Encore une fois, 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, cette solution intermédiaire vous permettra de gagner en efficacité sans avoir à vous soucier de rien.

27
rocketmonkeys

J'ai essayé la solution @Rocketmonkeys, mais les fichiers téléchargés étaient stockés sous le nom * .bin et recevaient des noms aléatoires. Ce n'est pas 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 à l’intérieur de/media ni/public_html) et les exposer via Django à certains utilisateurs ou dans certaines circonstances.
J'espère que ça aide.

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

16
Salvatorelab

Il suffit de mentionner l’objet FileResponse disponible dans Django 1.10

Edit: Je viens de rencontrer ma propre réponse alors que je cherchais un moyen simple de diffuser des fichiers via Django. Voici donc un exemple plus complet (pour moi à l’avenir). Il suppose que le nom du champ de fichiers 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'),
...
14
shadi

Il a été mentionné ci-dessus que la méthode mod_xsendfile n'autorise pas les caractères non-ASCII dans les noms de fichiers.

Pour cette raison, j’ai un correctif disponible pour mod_xsendfile qui autorisera l’envoi de tous les fichiers, à condition que le nom soit encodé en url et l’en-tête supplémentaire:

X-SendFile-Encoding: url

Est envoyé aussi bien.

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

13
btimby

Essayez: https://pypi.python.org/pypi/Django-sendfile/

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

7
Roberto Rosario

Vous devez utiliser les fichiers sendfile fournis par des serveurs populaires tels que Apache ou nginx en production. Plusieurs années, j’utilisais l’application sendfile de ces serveurs pour la protection des fichiers. Ensuite, nous avons créé une application Django basée sur un middleware simple et adaptée à cet effet, adaptée au développement et à la production. Vous pouvez accéder au code source ici .
UPDATE: dans la nouvelle version python le fournisseur utilise Django FileResponse s'il est disponible et ajoute également la prise en charge de nombreuses implémentations de serveur, de lighthttp, caddy à hiawatha.

Utilisation

pip install Django-fileprovider
  • ajouter fileprovider app aux paramètres INSTALLED_APPS,
  • ajouter fileprovider.middleware.FileProviderMiddleware aux MIDDLEWARE_CLASSES paramètres
  • réglez les paramètres FILEPROVIDER_NAME sur nginx ou Apache en production. Par défaut, il s'agit de python à des fins de développement.

dans vos vues de classe ou de fonction, définissez l'en-tête de réponse X-File valeur sur le chemin absolu du fichier. 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 mis en œuvre de manière à ce que votre code ne nécessite qu'une modification minimale.

Configuration de Nginx

Pour protéger le fichier des accès directs, vous pouvez définir la configuration comme suit:

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

Ici nginx définit l'URL de l'emplacement /files/ uniquement accès en interne, 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 nginx, le fichier sera protégé et vous pourrez également contrôler le fichier depuis Django views

6
I Am Batman

Django vous recommande d'utiliser un autre serveur pour desservir des supports statiques (un autre serveur s'exécutant sur le même ordinateur ne pose pas de problème.) Ils recommandent d'utiliser de tels serveurs en tant que lighttp .

C'est très simple à mettre en place. Pourtant. Si 'somefile.txt' est généré à la demande (le contenu est dynamique), vous pouvez souhaiter que Django le serve.

Django Docs - Fichiers statiques

2
kjfletch
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 
2

Fournir un accès protégé au dossier HTML statique à l'aide de https://github.com/johnsensible/Django-sendfile : https://Gist.github.com/iutinvg/9907731

0
iutinvg

Un autre projet à regarder: http://readthedocs.org/docs/Django-private-files/en/latest/usage.html Ça semble prometteur, je ne l'ai pas encore testé moi-même.

Fondamentalement, le projet résume la configuration de 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
thyagx

J'ai fait un projet à ce sujet. Vous pouvez regarder mon repo github:

https://github.com/nishant-boro/Django-rest-framework-download-expert

Ce module fournit un moyen simple de servir des fichiers à télécharger dans le cadre Django _ reste à l’aide du module Apache Xsendfile. Il offre également une fonctionnalité supplémentaire consistant à servir des téléchargements uniquement aux utilisateurs appartenant à un groupe particulier.

0
nicks_4317

J'ai rencontré le même problème plus d'une fois et j'ai donc implémenté le module xsendfile et les décorateurs de vues auth Django-filelibrary . N'hésitez pas à l'utiliser comme source d'inspiration pour votre propre solution.

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

0