Загрузка файлов¶
Когда 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})
Загрузка нескольких файлов¶
If you want to upload multiple files using one form field, create a subclass
of the field’s widget and set the allow_multiple_selected attribute on it
to 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()
Then override the post method of your
FormView subclass to handle multiple file
uploads:
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.
In previous versions, there was no support for the allow_multiple_selected
class attribute, and users were advised to create the widget with the HTML
attribute multiple set through the attrs argument. However, this
caused validation of the form field to be applied only to the last file
submitted, which could have adverse security implications.
Обработчики загрузки¶
Когда пользователь загружает файл, Django передает содержимое файла в обработчик загрузки – небольшой класс, который обрабатывает загруженные данные. Обработчики загрузки определенны в настройке FILE_UPLOAD_HANDLERS, по умолчанию:
["django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler"]
MemoryFileUploadHandler и TemporaryFileUploadHandler определяются обработку файлов по умолчанию в Django, загрузка небольших файлов в память и больших на диск.
Вы можете написать собственный обработчик загрузки файлов. Вы можете использовать его, например, для определения квоты для пользователей на загрузку файлов, сжатие данных «на лету», отображение прогресса загрузки файла, или пересылать файл в хранилище данных без сохранения его локально. Смотрите Написание пользовательских обработчиков загрузки, чтобы узнать как настроить или полностью переопределить поведение загрузки.
Где хранятся загруженный данные¶
Перед тем, как вы сохраните загруженный файл, загруженные данные должны где-то храниться.
По умолчанию, если загруженный файл меньше 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(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