Spec-Zone.ru › Django 5.1

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

При обработке загрузки файла 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()

Затем переопределите метод form_valid() вашего подкласса 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 form_valid(self, form):
        files = form.cleaned_data["file_field"]
        for f in files:
            ...  # Do something with each file.
        return super().form_valid(form)

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

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

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

При загрузке файла 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.1/topics/http/file-uploads/

Spec-Zone.ru

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