Comment télécharger un fichier dans Django? [fermé]

668

En tant que débutant à Django, j'ai du mal à créer une application de téléchargement dans Django 1.3. Je n'ai pas trouvé d'exemple / d'extraits à jour. Quelqu'un peut-il publier un exemple de code minimal mais complet (modèle, vue, modèle) pour le faire?

qliq
la source

Réponses:

1273

Ouf, la documentation Django n'a vraiment pas de bon exemple à ce sujet. J'ai passé plus de 2 heures à déterrer toutes les pièces pour comprendre comment cela fonctionne. Avec cette connaissance, j'ai mis en œuvre un projet qui permet de télécharger des fichiers et de les afficher sous forme de liste. Pour télécharger la source du projet, visitez https://github.com/axelpale/minimal-django-file-upload-example ou clonez-le:

> git clone https://github.com/axelpale/minimal-django-file-upload-example.git

Mise à jour 2013-01-30: La source de GitHub a également une implémentation pour Django 1.4 en plus de 1.3. Même s'il y a peu de changements, le tutoriel suivant est également utile pour 1.4.

Mise à jour 2013-05-10: Implémentation de Django 1.5 sur GitHub. Changements mineurs dans la redirection dans urls.py et utilisation de la balise de modèle d'URL dans list.html. Merci à hubert3 pour l'effort.

Mise à jour 2013-12-07: Django 1.6 pris en charge sur GitHub. Une importation a changé dans myapp / urls.py. Merci à Arthedian .

Mise à jour 2015-03-17: Django 1.7 pris en charge sur GitHub, grâce à aronysidoro .

Mise à jour 2015-09-04: Django 1.8 supporté sur GitHub, grâce à nerogit .

Mise à jour 2016-07-03: Django 1.9 pris en charge sur GitHub, grâce à daavve et nerogit

Arborescence du projet

Un projet Django 1.3 de base avec une seule application et un média / répertoire pour les téléchargements.

minimal-django-file-upload-example/
    src/
        myproject/
            database/
                sqlite.db
            media/
            myapp/
                templates/
                    myapp/
                        list.html
                forms.py
                models.py
                urls.py
                views.py
            __init__.py
            manage.py
            settings.py
            urls.py

1. Paramètres: myproject / settings.py

Pour télécharger et servir des fichiers, vous devez spécifier où Django stocke les fichiers téléchargés et à partir de quelle URL Django les sert. MEDIA_ROOT et MEDIA_URL sont dans settings.py par défaut mais ils sont vides. Voir les premières lignes de Django Managing Files pour plus de détails. N'oubliez pas également de définir la base de données et d'ajouter myapp à INSTALLED_APPS

...
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'database.sqlite3'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
...
INSTALLED_APPS = (
    ...
    'myapp',
)

2. Modèle: myproject / myapp / models.py

Ensuite, vous avez besoin d'un modèle avec un FileField. Ce champ particulier stocke par exemple des fichiers dans les médias / documents / 2011/12/24 / en fonction de la date actuelle et de MEDIA_ROOT. Voir la référence FileField .

# -*- coding: utf-8 -*-
from django.db import models

class Document(models.Model):
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')

3. Formulaire: myproject / myapp / forms.py

Pour bien gérer le téléchargement, vous avez besoin d'un formulaire. Ce formulaire ne comporte qu'un seul champ mais cela suffit. Voir la référence du formulaire FileField pour plus de détails.

# -*- coding: utf-8 -*-
from django import forms

class DocumentForm(forms.Form):
    docfile = forms.FileField(
        label='Select a file',
        help_text='max. 42 megabytes'
    )

4. Affichage: myproject / myapp / views.py

Une vue où toute la magie opère. Faites attention à la façon dont ils request.FILESsont traités. Pour moi, il était vraiment difficile de repérer le fait qu'il request.FILES['docfile']peut être enregistré dans des modèles. FileField comme ça. La fonction save () du modèle gère automatiquement le stockage du fichier dans le système de fichiers.

# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from myproject.myapp.models import Document
from myproject.myapp.forms import DocumentForm

def list(request):
    # Handle file upload
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            newdoc = Document(docfile = request.FILES['docfile'])
            newdoc.save()

            # Redirect to the document list after POST
            return HttpResponseRedirect(reverse('myapp.views.list'))
    else:
        form = DocumentForm() # A empty, unbound form

    # Load documents for the list page
    documents = Document.objects.all()

    # Render list page with the documents and the form
    return render_to_response(
        'myapp/list.html',
        {'documents': documents, 'form': form},
        context_instance=RequestContext(request)
    )

5. URL du projet: myproject / urls.py

Django ne sert pas MEDIA_ROOT par défaut. Ce serait dangereux dans un environnement de production. Mais au stade du développement, nous pourrions écourter. Faites attention à la dernière ligne. Cette ligne permet à Django de servir des fichiers à partir de MEDIA_URL. Cela ne fonctionne qu'en phase de développement.

Voir la référence django.conf.urls.static.static pour plus de détails. Voir également cette discussion sur la diffusion de fichiers multimédias .

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = patterns('',
    (r'^', include('myapp.urls')),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

6. URL d'application: myproject / myapp / urls.py

Pour rendre la vue accessible, vous devez lui spécifier des URL. Rien de spécial ici.

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url

urlpatterns = patterns('myapp.views',
    url(r'^list/$', 'list', name='list'),
)

7. Modèle: myproject / myapp / templates / myapp / list.html

La dernière partie: modèle pour la liste et le formulaire de téléchargement en dessous. Le formulaire doit avoir l'attribut enctype défini sur "multipart / form-data" et la méthode définie sur "post" pour permettre le téléchargement vers Django. Consultez la documentation relative aux téléchargements de fichiers pour plus de détails.

Le FileField possède de nombreux attributs qui peuvent être utilisés dans les modèles. Par exemple {{document.docfile.url}} et {{document.docfile.name}} comme dans le modèle. Pour en savoir plus, consultez l' article Utilisation des fichiers dans les modèles et la documentation de l'objet Fichier .

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Minimal Django File Upload Example</title>   
    </head>
    <body>
    <!-- List of uploaded documents -->
    {% if documents %}
        <ul>
        {% for document in documents %}
            <li><a href="{{ document.docfile.url }}">{{ document.docfile.name }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No documents.</p>
    {% endif %}

        <!-- Upload form. Note enctype attribute! -->
        <form action="{% url 'list' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <p>{{ form.non_field_errors }}</p>
            <p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
            <p>
                {{ form.docfile.errors }}
                {{ form.docfile }}
            </p>
            <p><input type="submit" value="Upload" /></p>
        </form>
    </body>
</html> 

8. Initialiser

Exécutez simplement syncdb et runserver.

> cd myproject
> python manage.py syncdb
> python manage.py runserver

Résultats

Enfin, tout est prêt. Dans l'environnement de développement Django par défaut, la liste des documents téléchargés peut être consultée sur localhost:8000/list/. Aujourd'hui, les fichiers sont téléchargés dans / path / to / myproject / media / documents / 2011/12/17 / et peuvent être ouverts à partir de la liste.

J'espère que cette réponse aidera quelqu'un autant qu'elle m'aurait aidé.

Akseli Palén
la source
9
Trouvé l'emplacement dans les documents django qui montre les téléchargements de fichiers. L'exemple de cette réponse est excellent, mais les informations contenues dans les documents django seront mises à jour avec les nouvelles versions. docs.djangoproject.com/en/dev/topics/http/file-uploads
TaiwanGrapefruitTea
1
L'exemple ne fonctionne pas pour Django "1.5". Dans le HTML {% url list %}devient {% url "list" %}.
Matthieu Riegler
4
Merci beaucoup . Ça marche vraiment pour moi. Cependant, pour les téléspectateurs à venir, vous devriez vérifier le code dans gitHub pour la meilleure compatibilité avec les nouvelles versions de Python et Django. Par exemple, le views.py, render_to_response () doit être remplacé par render (request, ...,) pour éviter l'erreur CSRF. À votre santé.
Huy Than
1
est-il possible de le faire sans FORMULAIRES?
Roel
1
Le fichier peut-il être .zip ou d'autres fichiers compressés?
qg_java_17137
74

De manière générale, lorsque vous essayez de «simplement obtenir un exemple de travail», il est préférable de «commencer à écrire du code». Il n'y a pas de code ici pour vous aider, donc cela fait beaucoup plus de travail pour nous de répondre à la question.

Si vous voulez récupérer un fichier, vous avez besoin de quelque chose comme ça dans un fichier html quelque part:

<form method="post" enctype="multipart/form-data">
    <input type="file" name="myfile" />
    <input type="submit" name="submit" value="Upload" />
</form>

Cela vous donnera le bouton de navigation, un bouton de téléchargement pour démarrer l'action (soumettre le formulaire) et notez le code afin que Django sache vous donner request.FILES

Dans une vue quelque part, vous pouvez accéder au fichier avec

def myview(request):
    request.FILES['myfile'] # this is my file

Il y a une énorme quantité d'informations dans les documents de téléchargement de fichiers

Je vous recommande de lire attentivement la page et de commencer à écrire du code - puis de revenir avec des exemples et des traces de pile lorsque cela ne fonctionne pas.

Henri
la source
10
Merci Henry. En fait, j'ai lu les documents et j'ai écrit du code, mais comme les documents présentent des lacunes (par exemple, "de quelque part import handle_uploaded_file") et que mon code était imparfait, je pensais que ce serait beaucoup mieux si je pouvais partir d'un exemple fonctionnel .
qliq
26
D'accord avec qliq. Un exemple de travail simple est le moyen le plus efficace de faire bouger les débutants, pas les docs
Philip007
11
Le enctype="multipart/form-data"ce dont j'avais besoin pour faire ce travail, merci!
john-charles
5
Ne manquez tout simplement pas le {% csrf_token%} dans les balises de formulaire.
jonincanada
il est possible de le faire SANS FORMULAIRES DE FORMS.PY?
Roel
71

Démo

Voir le dépôt github , fonctionne avec Django 3

Un exemple de téléchargement de fichier Django minimal

1. Créez un projet django

Exécutez startproject ::

$ django-admin.py startproject sample

maintenant un dossier ( exemple ) est créé.

2. créer une application

Créer une application ::

$ cd sample
$ python manage.py startapp uploader

Maintenant, un dossier ( uploader) avec ces fichiers est créé ::

uploader/
  __init__.py
  admin.py
  app.py
  models.py
  tests.py
  views.py
  migrations/
    __init__.py

3. Mettez à jour settings.py

Sur sample/settings.pyadd 'uploader'to INSTALLED_APPSet add MEDIA_ROOTand MEDIA_URL, ie:

INSTALLED_APPS = [
    'uploader',
    ...<other apps>...      
]

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

4. Mettez à jour urls.py

en sample/urls.pyplus ::

...<other imports>...
from django.conf import settings
from django.conf.urls.static import static
from uploader import views as uploader_views

urlpatterns = [
    ...<other url patterns>...
    path('', uploader_views.UploadView.as_view(), name='fileupload'),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

5. Mettez à jour models.py

mise à jour uploader/models.py::

from django.db import models
class Upload(models.Model):
    upload_file = models.FileField()    
    upload_date = models.DateTimeField(auto_now_add =True)

6. Mettre à jour views.py

mise à jour uploader/views.py::

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Upload
class UploadView(CreateView):
    model = Upload
    fields = ['upload_file', ]
    success_url = reverse_lazy('fileupload')
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['documents'] = Upload.objects.all()
        return context

7. créer des modèles

Créer un exemple de dossier / uploader / templates / uploader

Créez un fichier upload_form.html ie sample/uploader/templates/uploader/upload_form.html::

<div style="padding:40px;margin:40px;border:1px solid #ccc">
    <h1>Django File Upload</h1>
    <form method="post" enctype="multipart/form-data">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Submit</button>
    </form><hr>
    <ul>
    {% for document in documents %}
        <li>
            <a href="{{ document.upload_file.url }}">{{ document.upload_file.name }}</a>
            <small>({{ document.upload_file.size|filesizeformat }}) - {{document.upload_date}}</small>
        </li>
    {% endfor %}
    </ul>
</div>

8. Base de données Syncronize

Syncronize base de données et runserver ::

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver

visitez http: // localhost: 8000 /

suhailvs
la source
2
parfait sauf pour la dernière ligne - devrait être localhost.com:8000/upload > Cela a fonctionné pour django 1.6 et Python 3.3.
Steve
5
+1 pour le modèle de conception d'application django réutilisable
Marcel
1
Akseli a utilisé un FileFieldcertain temps suhail a utilisé un ImageField, quelqu'un pourrait-il expliquer les choix?
dvtan
@dtgq J'ai mis à jour la réponse à utiliser avec FileField. ImageFielddoit pour l'image uniquement télécharger. la mise à jour fonctionnera avec Django 1.11.
suhailvs
testé sur Django 2.0 et a parfaitement fonctionné
diek
29

Je dois dire que je trouve la documentation de django déroutante. Aussi pour l'exemple le plus simple, pourquoi les formulaires sont-ils mentionnés? L'exemple que j'ai pu travailler dans le views.py est: -

for key, file in request.FILES.items():
    path = file.name
    dest = open(path, 'w')
    if file.multiple_chunks:
        for c in file.chunks():
            dest.write(c)
    else:
        dest.write(file.read())
    dest.close()

Le fichier html ressemble au code ci-dessous, bien que cet exemple ne télécharge qu'un seul fichier et que le code pour enregistrer les fichiers en gère plusieurs: -

<form action="/upload_file/" method="post" enctype="multipart/form-data">{% csrf_token %}
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>

Ces exemples ne sont pas mon code, ils ont été choisis parmi deux autres exemples que j'ai trouvés. Je suis un débutant relatif à Django, il est donc très probable que je manque un point clé.

jimscafe
la source
3
+1 pour ne pas utiliser a FileFieldet a model.Form. Pour les débutants (et pour les tâches triviales), le traitement manuel des fichiers téléchargés comme indiqué ci-dessus est moins déroutant.
AneesAhmed777
dest = open (path, 'wb') lors de l'écriture de fichier avec octets
Bipul Roy
20

J'avais également la même exigence. La plupart des exemples sur net demandent de créer des modèles et des formulaires que je ne voulais pas utiliser. Voici mon code final.

if request.method == 'POST':
    file1 = request.FILES['file']
    contentOfFile = file1.read()
    if file1:
        return render(request, 'blogapp/Statistics.html', {'file': file1, 'contentOfFile': contentOfFile})

Et en HTML pour télécharger, j'ai écrit:

{% block content %}
    <h1>File content</h1>
    <form action="{% url 'blogapp:uploadComplete'%}" method="post" enctype="multipart/form-data">
         {% csrf_token %}
        <input id="uploadbutton" type="file" value="Browse" name="file" accept="text/csv" />
        <input type="submit" value="Upload" />
    </form>
    {% endblock %}

Voici le code HTML qui affiche le contenu du fichier:

{% block content %}
    <h3>File uploaded successfully</h3>
    {{file.name}}
    </br>content = {{contentOfFile}}
{% endblock %}
chetan pawar
la source
bon parce que parfois on veut juste utiliser le contenu du fichier sans sauvegarder le téléchargement ...
nemesisfixx
17

S'étendant sur l'exemple d'Henry :

import tempfile
import shutil

FILE_UPLOAD_DIR = '/home/imran/uploads'

def handle_uploaded_file(source):
    fd, filepath = tempfile.mkstemp(prefix=source.name, dir=FILE_UPLOAD_DIR)
    with open(filepath, 'wb') as dest:
        shutil.copyfileobj(source, dest)
    return filepath

Vous pouvez appeler cette handle_uploaded_filefonction depuis votre vue avec l'objet fichier téléchargé. Cela enregistrera le fichier avec un nom unique (préfixé par le nom de fichier du fichier téléchargé d'origine) dans le système de fichiers et renverra le chemin complet du fichier enregistré. Vous pouvez enregistrer le chemin dans la base de données et faire quelque chose avec le fichier plus tard.

Imran
la source
Imran, j'ai essayé votre code sur ma vue mais j'ai eu cette erreur: l'objet 'WSGIRequest' n'a pas d'attribut 'nom'.
qliq
2
Passez l'objet fichier téléchargé ( request.FILES['myfile']) à handle_uploaded_file, pas le requestlui - même.
Imran
Puis-je l'enregistrer directement dans la base de données? stackoverflow.com/questions/24705246/…
AlexandruC
En l'utilisant prefix=source.name, des caractères supplémentaires ont été ajoutés à la fin du fichier, gâchant l'extension du fichier. Par exemple, upload.csvchangé en upload.csv5334. Le changer pour le suffix=source.nameréparer pour moi.
Tahreem Iqbal
13

Ici, cela peut vous aider: créez un champ de fichier dans votre models.py

Pour télécharger le fichier (dans votre admin.py):

def save_model(self, request, obj, form, change):
    url = "http://img.youtube.com/vi/%s/hqdefault.jpg" %(obj.video)
    url = str(url)

    if url:
        temp_img = NamedTemporaryFile(delete=True)
        temp_img.write(urllib2.urlopen(url).read())
        temp_img.flush()
        filename_img = urlparse(url).path.split('/')[-1]
        obj.image.save(filename_img,File(temp_img)

et utilisez également ce champ dans votre modèle.

Vijesh Venugopal
la source
1
Ceci est utile lorsque vous devez tempérer manuellement avec des fichiers que vous souhaitez enregistrer. Si c'est le cas, vous pouvez également avoir besoin de cette section: docs.djangoproject.com/en/dev/topics/files/#the-file-object
kecske
11

Vous pouvez vous référer aux exemples de serveur dans Fine Uploader, qui a la version django. https://github.com/FineUploader/server-examples/tree/master/python/django-fine-uploader

Il est très élégant et le plus important de tous, il fournit la librairie js en vedette. Le modèle n'est pas inclus dans les exemples de serveur, mais vous pouvez trouver une démo sur son site Web. Fine Uploader: http://fineuploader.com/demos.html

django-fine-uploader

views.py

UploadView envoie la demande de publication et de suppression aux gestionnaires respectifs.

class UploadView(View):

    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super(UploadView, self).dispatch(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        """A POST request. Validate the form and then handle the upload
        based ont the POSTed data. Does not handle extra parameters yet.
        """
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_upload(request.FILES['qqfile'], form.cleaned_data)
            return make_response(content=json.dumps({ 'success': True }))
        else:
            return make_response(status=400,
                content=json.dumps({
                    'success': False,
                    'error': '%s' % repr(form.errors)
                }))

    def delete(self, request, *args, **kwargs):
        """A DELETE request. If found, deletes a file with the corresponding
        UUID from the server's filesystem.
        """
        qquuid = kwargs.get('qquuid', '')
        if qquuid:
            try:
                handle_deleted_file(qquuid)
                return make_response(content=json.dumps({ 'success': True }))
            except Exception, e:
                return make_response(status=400,
                    content=json.dumps({
                        'success': False,
                        'error': '%s' % repr(e)
                    }))
        return make_response(status=404,
            content=json.dumps({
                'success': False,
                'error': 'File not present'
            }))

forms.py

class UploadFileForm(forms.Form):

    """ This form represents a basic request from Fine Uploader.
    The required fields will **always** be sent, the other fields are optional
    based on your setup.
    Edit this if you want to add custom parameters in the body of the POST
    request.
    """
    qqfile = forms.FileField()
    qquuid = forms.CharField()
    qqfilename = forms.CharField()
    qqpartindex = forms.IntegerField(required=False)
    qqchunksize = forms.IntegerField(required=False)
    qqpartbyteoffset = forms.IntegerField(required=False)
    qqtotalfilesize = forms.IntegerField(required=False)
    qqtotalparts = forms.IntegerField(required=False)
Jiawei Dai
la source
7

Je ne sais pas s'il y a des inconvénients à cette approche, mais encore plus minime, dans views.py:

entry = form.save()

# save uploaded file
if request.FILES['myfile']:
    entry.myfile.save(request.FILES['myfile']._name, request.FILES['myfile'], True)
PhoebeB
la source
0

J'ai fait face au problème similaire et résolu par le site d'administration de django.

# models
class Document(models.Model):
    docfile = models.FileField(upload_to='documents/Temp/%Y/%m/%d')

    def doc_name(self):
        return self.docfile.name.split('/')[-1] # only the name, not full path

# admin
from myapp.models import Document
class DocumentAdmin(admin.ModelAdmin):
    list_display = ('doc_name',)
admin.site.register(Document, DocumentAdmin)
hlpmee
la source
[entrez la description du lien ici] [1] [entrez la description du lien ici] [2] [1]: youtu.be/0tNZB3dyopY [2]: youtu.be/klhMYMc3PlY
uda123