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

Когда 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 в конструктор формы.

Обработка загруженных файлов

class UploadedFile

Последняя часть головоломки – обработка данных файлов из request.FILES. Каждый элемент словаря является объектом UploadedFile – простая обертка над загруженным файлом, которая содержит методы для доступа к загруженным данным:

read()

Читает все загруженные данные из файла. Будьте осторожны с этим методом: если загруженный файл большой, загрузка его в память может сильно нагрузить вашу систему. Лучше использовать метод chunks(), описанные ниже.

multiple_chunks()

Возвращает True, если загруженный файл достаточно большой, чтобы читать его по частям. По умолчанию это файл больше 2.5 мегабайт, но это можно настроить(смотрите ниже).

chunks()

Генератор, который возвращает часть(chunk) файла. Если multiple_chunks() равен True, вы должны использовать этот метод в цикле вместо метода read().

На практике, чаще всего проще всегда использовать метод chunks(), смотрите пример ниже.

name

Название загруженного файла (например, my_file.txt).

size

Размер, в байтах, загруженного файла.

Объект UploadedFile содержит и другие методы и атрибуты, смотрите описание `UploadedFile objects`_.

Соберем все вместе и вот пример, как обработать загруженный файл:

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(), обезопасит вас от нагрузки системы при загрузке большого файла.

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

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

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

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

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

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

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

FILE_UPLOAD_MAX_MEMORY_SIZE

Максимальный размер файла, в байтах, который будет загружен в память. Файлы больше чем FILE_UPLOAD_MAX_MEMORY_SIZE будут загружены на диск.

Значение по умолчанию 2.5 мегабайта.

FILE_UPLOAD_TEMP_DIR

Каталог, в который сохраняются загруженные файлы больше чем FILE_UPLOAD_MAX_MEMORY_SIZE.

По умолчанию - системная папка для временных файлов (например, /tmp на большинстве Unix-систем).

FILE_UPLOAD_PERMISSIONS

Числовое представление прав на файл (например, 0644), которые устанавливаются для загруженных файлов. Подробности можно прочитать в описании функции os.chmod().

Если не указана или равно None, будет зависеть от системы, которую вы используете. В большинстве систем, временные файлы сохраняются с правами 0600. Файлы сохраненные с памяти используют системное значение umask.

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

Если вы не знакомы с правами доступа, запомните что 0 очень важен: это указывает на восьмеричное число, которые используются для определения прав. Если вы укажите 644, все будет работать неправильно.

Всегда добавляйте 0 в начале.

FILE_UPLOAD_HANDLERS

Обработчик загрузки файлов. Переопределение этой настройки позволит вам полностью изменить процесс загрузки файлов в Django. Смотрите описание `upload handlers`_ ниже.

По умолчанию:

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

Что означает “попробуем сначала загрузить в память, а потом во временной файл.”

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

Если вы сохраняете файлы для модели 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})

Объект UploadedFile

В дополнение к атрибутам и методам определенным в классе File, базовом классе класса UploadedFile, также дополнительно определены:

UploadedFile.content_type

“Content-Type” заголовок, переданный с загруженным файлом (например, text/plain или application/pdf). Как и всем данным предоставленным пользователям, вы не должны доверять этому значению. Вам по-прежнему необходимо проверять, что содержимое файла отвечает указанному “Content-Type” – “доверяй, но проверяй”.

UploadedFile.charset

Для файлов с MIME-типом text/*, кодировка (например, utf8) указанная браузером. Опять же, “доверяй, но проверяй”.

UploadedFile.temporary_file_path

Только файлы загруженные на диск будут содержать этот метод. Возвращает полный путь к временному файлу.

Примечание

Как и с обычным Python файлом, вы можете прочитать его содержимое строкой за строкой в цикле:

for line in uploadedfile:
    do_something_with(line)

Однако, в отличии от стандартного Python файла, UploadedFile понимает только \n (также известный как “Unix-стиль”) перенос строки. Если вам необходимо обработать файл с разными стилями переноса строки, вы должны сделать это самостоятельно.

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

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

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

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

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

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

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

Создание собственного обработчика загрузки

Все обработчики загрузки файлов должны наследоваться от django.core.files.uploadhandler.FileUploadHandler. Вы можете определять их в любом модуле.

Обязательные методы

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

FileUploadHandler.receive_data_chunk(self, raw_data, start)

Получение “части” данных загружаемого файла.

raw_data – байтовая строка с загружаемыми данными.

start – указывает на расположения данных из raw_data в файле.

Данные, возвращаемые этим методом будут переданы в метод receive_data_chunk следующих обработчиков. То есть один обработчик может быть “фильтром” для других.

Верните None из receive_data_chunk, если последующие обработчики не должны обрабатывать эти данные. Это может быть полезным, если данные сохраняются в вашем обработчике и вы не хотите, чтобы последующие сохраняли еще одну копию данных.

При вызове исключения StopUpload или SkipFile, загрузка будет остановлена или файл будет полностью проигнорирован.

FileUploadHandler.file_complete(self, file_size)

Вызывается после окончания загрузки файла.

Обработчик должен вернуть объект UploadedFile, который будет добавлен в request.FILES. Обработчик может вернуть None указав, что объект UploadedFile должен быть получен из последующих обработчиков.

Необязательные методы

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

FileUploadHandler.chunk_size

Размер, в байтах, “части” файла, которую Django должен сохранить в памяти и передать в обработчик. То есть, этот атрибут контролирует размер “асти файла, которая передается в FileUploadHandler.receive_data_chunk.

Для максимальной производительности это значение должно быть кратным 4 и не превышать 2 GB (231 байтов). Если несколько обработчик указывают разное значение, Django будет использовать самое маленькое.

Значение по умолчанию 64*210 байтов, или 64 KB.

FileUploadHandler.new_file(self, field_name, file_name, content_type, content_length, charset)

Вызывает перед началом загрузки, до того, как данные будут переданы в один из обработчиков.

field_name строковое название поля <input>.

file_name название файла в unicode, предоставленное браузером.

content_type – MIME-тип, предоставленный браузером – например, 'image/jpeg'.

content_length – размер изображения, предоставленный браузером. Может быть не предоставлен и значение будет равным None.

charset – кодировка (например, utf8), предоставленная браузером. Как и content_length может быть пустым.

Этот метод может вызвать исключение StopFutureHandlers и остановить последующую обработку файла всем обработчиками.

FileUploadHandler.upload_complete(self)

Вызывается после полной загрузки всех файлов.

FileUploadHandler.handle_raw_input(self, input_data, META, content_length, boundary, encoding)

Позволяет полностью переопределить обработку “сырых” HTTP-данных.

input_data – объект с интерфейсом файла с данными.

META – объект из request.META.

content_length – размер данных из input_data. Не читайте больше чем content_length байтов из input_data.

boundary – “MIME boundary” запроса.

encoding – кодировка запроса.

Верните None для продолжения обработки, или кортеж (POST, FILES), который будет добавлен в объект запроса.