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

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

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

Принимать загруженный контент от непроверенных пользователей – опасно! Подробности смотрите в Контент, загружаемый пользователями.

Основы загрузки файла

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

# In 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 в форму, как описано в разделе Привязка загруженных файлов к форме. Это будет выглядеть следующим образом:

from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
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_to_response('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})

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

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

("django.core.files.uploadhandler.MemoryFileUploadHandler",
 "django.core.files.uploadhandler.TemporaryFileUploadHandler",)

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

Вы можете написать собственный обработчик загрузки файлов. Вы можете использовать его, например, для определения квоты для пользователей на загрузку файлов, сжатие данных “на лету”, отображение прогресса загрузки файла, или пересылать файл в хранилище данных без сохранения его локально. Смотрите Writing custom upload handlers, чтобы узнать как настроить или полностью переопределить поведение загрузки.

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

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

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

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

Эти значения – 2.5 мегабайта, /tmp и др. – просто «разумные значения по умолчанию”, которое вы можете переопределить. Об этом в следующем разделе.

Изменение процесса загрузки

Процесс загрузки файлов использует несколько настроек. Подробности смотрите в разделе настроек.

Изменение обработчиков загрузки файла “на лету”

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

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

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

Использование list.insert() в этом случае (вместо append()) буде уместней, так как обработчик, уведомляющий о прогрессе загрузки, должен использоваться перед другими обработчиками. Запомните, обработчики загрузки используются по-порядку.

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

request.upload_handlers = [ProgressBarUploadHandler()]

Примечание

Вы можете изменить обработчики загрузок перед использованием 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())
    return _upload_file_view(request)

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