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?
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.
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.
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.
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))
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=)
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é.
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'),
...
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."
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
appINSTALLED_APPS
paramètres", - ajouter
fileprovider.middleware.FileProviderMiddleware
àMIDDLEWARE_CLASSES
paramètres - set
FILEPROVIDER_NAME
paramètresnginx
ouapache
dans la production, par défaut c'estpython
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
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.
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
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)
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.
fournissant un accès protégé au dossier html statique en utilisant https://github.com/johnsensible/django-sendfile : https://gist.github.com/iutinvg/9907731