Spec-Zone.ru › Django 5.0

Загрузка файлов

При обработке загрузки файла Django помещает данные файла в request.FILES (для получения дополнительной информации об объекте request см. документацию по объектам запроса и ответа). В этом документе описано, как файлы хранятся на диске и в памяти, а также как настроить стандартное поведение.

Предупреждение

Существуют риски безопасности, если вы принимаете загруженные данные от незащищенных пользователей! См. раздел руководства по безопасности о безопасности данных, загружаемых пользователями для получения информации о мерах по снижению рисков.

Базовая загрузка файлов

Рассмотрим форму, содержащую FileField:

forms.py
from django import forms


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

Представление, обрабатывающее эту форму, получит данные файла в request.FILES, который является словарем, содержащим ключ для каждого FileField (или ImageField, или другого подкласса FileField) в форме. Таким образом, данные из указанной выше формы будут доступны как request.FILES['file'].

Обратите внимание, что request.FILES будет содержать данные только в том случае, если метод запроса был POST, по крайней мере одно поле файла было фактически отправлено и у <form> , отправившего запрос, есть атрибут enctype="multipart/form-data". В противном случае request.FILES будет пустым.

В большинстве случаев вы передаете данные файла из request в форму, как описано в Связывание загруженных файлов с формой. Это будет выглядеть примерно так:

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})

Обратите внимание, что мы должны передать request.FILES в конструктор формы; именно так данные файлов связываются с формой.

Вот распространенный способ обработки загруженного файла:

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

Использование цикла по UploadedFile.chunks() вместо read() гарантирует, что большие файлы не перегрузят оперативную память вашей системы.

Для объектов UploadedFile доступны несколько других методов и атрибутов; см. UploadedFile для получения полной справки.

Обработка загруженных файлов с помощью модели

Если вы сохраняете файл в Model с помощью FileField, использование ModelForm значительно упрощает этот процесс. Объект файла будет сохранен в указанном расположении аргументом upload_to соответствующего FileField при вызове 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})

Если вы создаёте объект вручную, вы можете назначить объект файла из request.FILES полю файла в модели:

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})

Если вы создаёте объект вручную вне запроса, вы можете назначить объект наподобие File полю 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()

Загрузка нескольких файлов

Если вы хотите загрузить несколько файлов с помощью одного поля формы, создайте подкласс виджета поля и установите атрибут allow_multiple_selected в True.

Для того, чтобы все такие файлы были валидированы вашей формой (и чтобы значение поля включало их все), вам также необходимо создать подкласс FileField. Ниже приведен пример.

Поле для загрузки нескольких файлов

В будущем Django, вероятно, будет поддерживать загрузку нескольких файлов надлежащим образом.

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()

Затем переопределите метод post своего подкласса FormView, чтобы обработать загрузку нескольких файлов:

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 post(self, request, *args, **kwargs):
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

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

Предупреждение

Это позволит обрабатывать несколько файлов только на уровне формы. Имейте в виду, что вы не можете использовать его для размещения нескольких файлов в одном экземпляре модели (в одном поле), например, даже если пользовательский виджет используется с полем формы, связанным с моделью FileField.

Изменено в Django 3.2.19:

В предыдущих версиях атрибут класса allow_multiple_selected не поддерживался, и пользователям советовали создать виджет с атрибутом HTML multiple, заданным через аргумент attrs. Однако это приводило к тому, что валидация поля формы применялась только к последнему отправленному файлу, что может иметь негативные последствия для безопасности.

Обработчики загрузки

При загрузке пользователем файла Django передает данные файла обработчику загрузки — небольшому классу, который обрабатывает данные файла во время загрузки. Обработчики загрузки изначально определяются в настройке FILE_UPLOAD_HANDLERS, которая по умолчанию:

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

Вместе MemoryFileUploadHandler и TemporaryFileUploadHandler обеспечивают стандартное поведение загрузки файлов в Django, читая небольшие файлы в память, а большие — на диск.

Вы можете написать пользовательские обработчики, которые настроят способ обработки файлов Django. Например, вы можете использовать пользовательские обработчики для применения квот на уровне пользователя, сжатия данных в режиме реального времени, отображения индикаторов выполнения и даже отправки данных в другое место хранения напрямую без локального сохранения. Подробнее о настройке или полном замещении поведения загрузки см. в разделе Создание пользовательских обработчиков загрузки.

Где хранятся загруженные данные

Перед сохранением загруженных файлов данные необходимо сохранить где-то.

По умолчанию, если загруженный файл меньше 2,5 мегабайт, Django сохранит всё содержимое загрузки в памяти. Это означает, что сохранение файла сводится только к чтению из памяти и записи на диск, что очень быстро.

Однако, если загруженный файл слишком большой, Django запишет загруженный файл во временный файл, хранящийся в временной папке вашей системы. В Unix-подобных системах это означает, что Django может создать файл с именем примерно /tmp/tmpzfp6I6.upload. Если загрузка достаточно большая, вы можете наблюдать, как размер этого файла увеличивается по мере того, как Django записывает данные на диск.

Эти особенности — 2,5 мегабайта; /tmp; и т. д. — являются «разумными значениями по умолчанию», которые могут быть настраиваемы, как описано в следующем разделе.

Изменение поведения обработчиков загрузки

Существует несколько настроек, которые контролируют поведение загрузки файлов в Django. Подробности см. в разделе Настройки загрузки файлов.

Изменение обработчиков загрузки во время выполнения

Иногда определённые представления требуют другого поведения загрузки. В таких случаях вы можете переопределить обработчики загрузки на основе запроса, изменив request.upload_handlers. По умолчанию этот список будет содержать обработчики загрузки, заданные в FILE_UPLOAD_HANDLERS, но вы можете изменить список так же, как и любой другой список.

Например, предположим, что вы написали обработчик ProgressBarUploadHandler , который предоставляет обратную связь о прогрессе загрузки для некоторого виджета AJAX. Вы добавите этот обработчик в свои обработчики загрузки следующим образом:

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

В этом случае вы, вероятно, захотите использовать list.insert() (вместо append()), поскольку обработчик прогресса должен выполняться до любых других обработчиков. Помните, обработчики загрузки обрабатываются в порядке следования.

Если вы хотите полностью заменить обработчики загрузки, вы можете назначить новый список:

request.upload_handlers = [ProgressBarUploadHandler(request)]

Примечание

Вы можете изменить обработчики загрузки только до доступа к request.POST или request.FILES – изменять обработчики загрузки после начала обработки загрузки не имеет смысла. Если вы попытаетесь изменить request.upload_handlers после чтения из request.POST или request.FILES, Django выдаст ошибку.

Таким образом, всегда необходимо изменять обработчики загрузки как можно раньше в вашем представлении.

Также, к request.POST обращается CsrfViewMiddleware, который включен по умолчанию. Это означает, что вам необходимо использовать csrf_exempt() в вашем представлении, чтобы разрешить изменение обработчиков загрузки. Затем вам необходимо использовать csrf_protect() в функции, которая фактически обрабатывает запрос. Обратите внимание, что это означает, что обработчики могут начать получать загрузку файла до выполнения проверок CSRF. Пример кода:

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
    ...

Если вы используете представление на основе класса, вам необходимо использовать csrf_exempt() в его методе dispatch() и csrf_protect() в методе, который фактически обрабатывает запрос. Пример кода:

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
        ...

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.0/topics/http/file-uploads/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API