# Documentation Django 6.0.7

# Envoi de fichiers

Lorsque Django gère un téléversement de fichier, les données du fichier aboutissent dans `request.FILES` (plus de détails sur l’objet `request` se trouvent dans la documentation des objets request et response). Ce document explique comment les fichiers sont stockés sur disque et en mémoire, et comment personnaliser le comportement par défaut.

Avertissement

Il existe des risques de sécurité si vous acceptez du contenu téléversé de la part d’utilisateurs non fiables. Consultez le sujet Contenu envoyé par les utilisateurs du guide de sécurité pour des détails sur les mesures de réduction des risques.

## Envoi simple de fichiers

Considérez un formulaire contenant un champ `FileField`:

`forms.py`

    from django import forms

    class UploadFileForm(forms.Form):
        title = forms.CharField(max_length=50)
        file = forms.FileField()

Une vue gérant ce formulaire recevra les données de fichier dans `request.FILES` qui est un dictionnaire contenant une clé pour chaque `FileField` (ou `ImageField`, ou toute autre sous-classe de `FileField`) du formulaire. Ainsi, les données du formulaire ci-dessus seraient accessibles dans `request.FILES['file']`.

Notez que `request.FILES` ne contient les données que lorsque la méthode de requête est `POST`, qu’au moins un fichier est contenu dans l’envoi et que le formulaire``<form>`` à l’origine de la requête possède l’attribut `enctype="multipart/form-data"`. Sinon, `request.FILES` est vide.

La plupart du temps, les données de fichiers seront transmises de `request` au formulaire comme expliqué dans Liaison de fichiers téléversés avec un formulaire. Voici ce que cela peut donner :

`views.py`

    from django.http import HttpResponseRedirect
    from django.shortcuts import render
    from .forms import UploadFileForm

    # Imaginary function to handle an uploaded file.
    from somewhere import handle_uploaded_file

    def upload_file(request):
        if request.method == "POST":
            form = UploadFileForm(request.POST, request.FILES)
            if form.is_valid():
                handle_uploaded_file(request.FILES["file"])
                return HttpResponseRedirect("/success/url/")
        else:
            form = UploadFileForm()
        return render(request, "upload.html", {"form": form})

Remarquez que nous devons transmettre `request.FILES` au constructeur du formulaire ; c’est comme cela que les données de fichiers sont liées à un formulaire.

Voici une manière habituelle de gérer un fichier téléversé :

    def handle_uploaded_file(f):
        with open("some/file/name.txt", "wb+") as destination:
            for chunk in f.chunks():
                destination.write(chunk)

En bouclant sur `UploadedFile.chunks()` au lieu d’appeler `read()`, on peut s’assurer que les gros fichiers ne saturent pas la mémoire du système.

Il existe quelques autres méthodes et attributs accessibles pour les objets `UploadedFile`. Voir `UploadedFile` pour une référence complète.

### Téléversement de fichiers liés à un modèle

Si vous enregistrez un fichier dans un `Model` contenant un champ `FileField`, l’emploi d’un formulaire `ModelForm` simplifie le processus. L’objet fichier sera enregistré à l’emplacement indiqué par le paramètre `upload_to` du champ `FileField` correspondant lors de l’appel à `form.save()`:

    from django.http import HttpResponseRedirect
    from django.shortcuts import render
    from .forms import ModelFormWithFileField

    def upload_file(request):
        if request.method == "POST":
            form = ModelFormWithFileField(request.POST, request.FILES)
            if form.is_valid():
                # file is saved
                form.save()
                return HttpResponseRedirect("/success/url/")
        else:
            form = ModelFormWithFileField()
        return render(request, "upload.html", {"form": form})

Si vous construisez manuellement un objet, vous pouvez attribuer l’objet fichier provenant de `request.FILES` au champ de fichier du modèle :

    from django.http import HttpResponseRedirect
    from django.shortcuts import render
    from .forms import UploadFileForm
    from .models import ModelWithFileField

    def upload_file(request):
        if request.method == "POST":
            form = UploadFileForm(request.POST, request.FILES)
            if form.is_valid():
                instance = ModelWithFileField(file_field=request.FILES["file"])
                instance.save()
                return HttpResponseRedirect("/success/url/")
        else:
            form = UploadFileForm()
        return render(request, "upload.html", {"form": form})

Si vous construisez un objet manuellement en dehors d’une requête, vous pouvez attribuer un objet du genre `File` au champ `FileField`:

    from django.core.management.base import BaseCommand
    from django.core.files.base import ContentFile

    class MyCommand(BaseCommand):
        def handle(self, *args, **options):
            content_file = ContentFile(b"Hello world!", name="hello-world.txt")
            instance = ModelWithFileField(file_field=content_file)
            instance.save()

### Téléversement de fichiers multiples

Si vous souhaitez pouvoir téléverser plusieurs fichiers avec un seul champ de formulaire, créez une sous-classe du composant de champ et définissez son attribut de classe `allow_multiple_selected` à `True`.

Afin que de tels fichiers soient tous validés par votre formulaire (et que la valeur du champ les inclue tous), vous devrez aussi créer une sous-classe du champ `FileField`. Voir ci-dessous pour un exemple.

Champ de fichiers multiples

Il est probable que Django propose un champ pour des fichiers multiples dans un avenir pas trop lointain.

`forms.py`

    from django import forms

    class MultipleFileInput(forms.ClearableFileInput):
        allow_multiple_selected = True

    class MultipleFileField(forms.FileField):
        def __init__(self, *args, **kwargs):
            kwargs.setdefault("widget", MultipleFileInput())
            super().__init__(*args, **kwargs)

        def clean(self, data, initial=None):
            single_file_clean = super().clean
            if isinstance(data, (list, tuple)):
                result = [single_file_clean(d, initial) for d in data]
            else:
                result = [single_file_clean(data, initial)]
            return result

    class FileFieldForm(forms.Form):
        file_field = MultipleFileField()

Puis surchargez la méthode `form_valid()` de votre sous-classe de `FormView` pour pouvoir gérer plusieurs fichiers reçus :

`views.py`

    from django.views.generic.edit import FormView
    from .forms import FileFieldForm

    class FileFieldFormView(FormView):
        form_class = FileFieldForm
        template_name = "upload.html"  # Replace with your template.
        success_url = "..."  # Replace with your URL or reverse().

        def form_valid(self, form):
            files = form.cleaned_data["file_field"]
            for f in files:
                ...  # Do something with each file.
            return super().form_valid(form)

Avertissement

Ceci vous permet de gérer des fichier multiples purement au niveau du formulaire. Notez que vous ne pouvez pas l’utiliser pour inclure plusieurs fichiers dans une seule instance de modèle (dans un seul champ), par exemple, même si le composant personnalisé est utilisé avec un champ de formulaire lié à un champ de modèle de type `FileField`.

## Gestionnaires de téléversement

Lorsqu’un utilisateur envoie un fichier, Django transmet les données du fichier à un gestionnaire de téléversement, une petite classe qui gère les données du fichier reçu. Les gestionnaires de téléversement sont définis initialement dans le réglage `FILE_UPLOAD_HANDLERS` dont le contenu par défaut est :

    [
        "django.core.files.uploadhandler.MemoryFileUploadHandler",
        "django.core.files.uploadhandler.TemporaryFileUploadHandler",
    ]

Conjointement, `MemoryFileUploadHandler` et `TemporaryFileUploadHandler` définissent le comportement Django par défaut de téléversement de fichier en plaçant les petits fichiers en mémoire et les plus gros sur le disque.

Vous pouvez écrire des gestionnaires personnalisée qui modifient la façon de gérer les fichiers. Vous pourriez par exemple utiliser des gestionnaires personnalisés pour limiter la quantité de données par utilisateur, compresser les données à la volée, afficher des barres de progression ou même envoyer directement des données vers un autre emplacement de stockage sans les stocker localement. Voir Écriture de gestionnaires de téléversement personnalisés pour plus de détails sur la manière de personnaliser ou de remplacer complètement le comportement de téléversement.

### Emplacement de stockage des données

Avant d’enregistrer des fichiers téléversés, les données doivent bien être stockées quelque part.

Par défaut, si un fichier téléversé est plus petit que 2,5 Mio, Django place la totalité du fichier en mémoire. Cela signifie que l’enregistrement du fichier correspond à une simple lecture en mémoire et à une écriture sur le disque, ce qui est très rapide.

Cependant, si un fichier téléversé est trop gros, Django écrit le fichier dans un fichier temporaire stocké dans le répertoire des fichiers temporaires du système. Sur une plate-forme de type Unix, Django va en principe générer un fichier dont le chemin correspond à quelque chose comme `/tmp/tmpzfp6I6.upload`. Si le fichier est suffisamment gros, vous pouvez même voir la taille du fichier augmenter au fur et à mesure de son flux d’enregistrement vers le disque.

Ces paramètres spécifiques, 2,5 Mio, `/tmp`, etc. ne sont que des valeurs par défaut « raisonnables » qui peuvent être personnalisés comme expliqué dans la section suivante.

### Modification de la gestion des téléversements

Certains réglages permettent de contrôler le comportement de téléversement de fichiers de Django. Voir Réglages de téléversement de fichiers pour plus de détails.

### Modification des gestionnaires de téléversement à la volée

Parfois, certaines vues ont besoin d’un comportement de téléversement différent. Dans ces situations, il est possible de surcharger les gestionnaires de téléversement par requête en modifiant `request.upload_handlers`. Par défaut, celle liste contient les gestionnaires de téléversement énumérés dans `FILE_UPLOAD_HANDLERS`, mais cette liste est modifiable comme n’importe quelle autre liste.

Par exemple, supposons que vous ayez écrit un gestionnaire `ProgressBarUploadHandler` qui renvoie des informations de progression de téléversement d’un composant AJAX quelconque. Voici comment ajouter ce gestionnaire à la liste des gestionnaires de téléversement :

    request.upload_handlers.insert(0, ProgressBarUploadHandler(request))

L’emploi de `list.insert()`, comme montré ci-dessus, assure que le gestionnaire de la barre de progression est placé au début de la liste. Comme les gestionnaires de téléversements sont exécutés l’un après l’autre, ce placement garantit que le gestionnaire s’exécute avant les gestionnaires par défaut, ce qui lui permet de suivre la progression durant l’entier du téléchargement.

Si vous vouliez totalement remplacer les gestionnaires de téléversement, il suffirait d’attribuer une nouvelle liste :

    request.upload_handlers = [ProgressBarUploadHandler(request)]

Note

Il n’est possible de modifier les gestionnaires de téléversement qu”avant d’accéder à `request.POST` ou `request.FILES`, car ça n’aurait pas de sens de modifier les gestionnaires de téléversement après que la gestion des téléversements a déjà démarré. Si vous le faites tout de même, Django générera une erreur.

Ceci dit, la modification des gestionnaires de téléversement devrait toujours intervenir aussi tôt que possible dans les vues.

De plus, `CsrfViewMiddleware` qui est un intergiciel activé par défaut accède à `request.POST`. Cela signifie qu’il est nécessaire de décorer avec `csrf_exempt()` les vues dans lesquelles vous souhaitez changer les gestionnaires de téléversement. Il faut ensuite utiliser `csrf_protect()` sur la fonction qui traite effectivement la requête. Remarquez qu’il se pourrait que les gestionnaires commencent à recevoir un fichier envoyé avant que les contrôles CSRF aient été effectués. Exemple de code :

    from django.views.decorators.csrf import csrf_exempt, csrf_protect

    @csrf_exempt
    def upload_file_view(request):
        request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
        return _upload_file_view(request)

    @csrf_protect
    def _upload_file_view(request):
        # Process request
        ...

Si vous utilisez une vue basée sur une classe, vous devrez utiliser `csrf_exempt()` sur sa méthode `dispatch()` et `csrf_protect()` sur la méthode qui traite réellement la requête. Code d’exemple

    from django.utils.decorators import method_decorator
    from django.views import View
    from django.views.decorators.csrf import csrf_exempt, csrf_protect

    @method_decorator(csrf_exempt, name="dispatch")
    class UploadFileView(View):
        def setup(self, request, *args, **kwargs):
            request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
            super().setup(request, *args, **kwargs)

        @method_decorator(csrf_protect)
        def post(self, request, *args, **kwargs):
            # Process request
            ...