У меня есть форма в моем приложении django, где пользователи могут загружать файлы.
Как я могу установить ограничение на размер загруженного файла, чтобы, если пользователь загружает файл, превышающий мой лимит, форма будет недействительной и она выведет ошибку?
Ограничение размера загрузки файла Django
Ответ 1
Этот код может помочь:
# Add to your settings file
CONTENT_TYPES = ['image', 'video']
# 2.5MB - 2621440
# 5MB - 5242880
# 10MB - 10485760
# 20MB - 20971520
# 50MB - 5242880
# 100MB 104857600
# 250MB - 214958080
# 500MB - 429916160
MAX_UPLOAD_SIZE = "5242880"
#Add to a form containing a FileField and change the field names accordingly.
from django.template.defaultfilters import filesizeformat
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
def clean_content(self):
content = self.cleaned_data['content']
content_type = content.content_type.split('/')[0]
if content_type in settings.CONTENT_TYPES:
if content._size > settings.MAX_UPLOAD_SIZE:
raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(settings.MAX_UPLOAD_SIZE), filesizeformat(content._size)))
else:
raise forms.ValidationError(_('File type is not supported'))
return content
Взято из: Django Snippets - проверяет тип и размер содержимого файла
Ответ 2
Вы можете использовать этот фрагмент formatChecker. Что он делает, это
-
он позволяет указать, какие форматы файлов разрешены для загрузки.
-
и позволяет установить ограничение на размер файла загружаемого файла.
Во-первых. Создайте файл с именем formatChecker.py внутри приложения, где у вас есть модель с файловым полем, для которой вы хотите принять определенный тип файла.
Это ваш formatChecker.py:
from django.db.models import FileField
from django.forms import forms
from django.template.defaultfilters import filesizeformat
from django.utils.translation import ugettext_lazy as _
class ContentTypeRestrictedFileField(FileField):
"""
Same as FileField, but you can specify:
* content_types - list containing allowed content_types. Example: ['application/pdf', 'image/jpeg']
* max_upload_size - a number indicating the maximum file size allowed for upload.
2.5MB - 2621440
5MB - 5242880
10MB - 10485760
20MB - 20971520
50MB - 5242880
100MB 104857600
250MB - 214958080
500MB - 429916160
"""
def __init__(self, *args, **kwargs):
self.content_types = kwargs.pop("content_types")
self.max_upload_size = kwargs.pop("max_upload_size")
super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
data = super(ContentTypeRestrictedFileField, self).clean(*args, **kwargs)
file = data.file
try:
content_type = file.content_type
if content_type in self.content_types:
if file._size > self.max_upload_size:
raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(self.max_upload_size), filesizeformat(file._size)))
else:
raise forms.ValidationError(_('Filetype not supported.'))
except AttributeError:
pass
return data
Во-вторых. В вашем файле models.py добавьте следующее:
from formatChecker import ContentTypeRestrictedFileField
Затем вместо использования "FileField" используйте этот "ContentTypeRestrictedFileField".
Пример:
class Stuff(models.Model):
title = models.CharField(max_length=245)
handout = ContentTypeRestrictedFileField(upload_to='uploads/', content_types=['video/x-msvideo', 'application/pdf', 'video/mp4', 'audio/mpeg', ],max_upload_size=5242880,blank=True, null=True)
Вы можете изменить значение 'max_upload_size' до предела размера файла, который вы хотите. Вы также можете изменить значения внутри списка "content_types" на типы файлов, которые вы хотите принять.
Ответ 3
другое решение использует валидаторы
from django.core.exceptions import ValidationError
def file_size(value): # add this to some file where you can import it from
limit = 2 * 1024 * 1024
if value.size > limit:
raise ValidationError('File too large. Size should not exceed 2 MiB.')
то в вашей форме с полем "Файл" у вас есть что-то вроде этого
image = forms.FileField(required=False, validators=[file_size])
Ответ 4
Я считаю, что форма django получает файл только после его полной загрузки. Поэтому, если кто-то загружает файл 2Gb, вам намного лучше с проверкой размера веб-сервера на лету.
Подробнее см. почтовый поток.
Ответ 5
Просто короткая заметка о фрагменте, который был включен в этот поток:
Взгляните на этот фрагмент: http://www.djangosnippets.org/snippets/1303/
Это было очень полезно, однако оно включало несколько незначительных ошибок. Более надежный код должен выглядеть следующим образом:
# Add to your settings file
CONTENT_TYPES = ['image', 'video']
# 2.5MB - 2621440
# 5MB - 5242880
# 10MB - 10485760
# 20MB - 20971520
# 50MB - 5242880
# 100MB - 104857600
# 250MB - 214958080
# 500MB - 429916160
MAX_UPLOAD_SIZE = "5242880"
#Add to a form containing a FileField and change the field names accordingly.
from django.template.defaultfilters import filesizeformat
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
def clean_content(self):
if content != None:
content = self.cleaned_data['content']
content_type = content.content_type.split('/')[0]
if content_type in settings.CONTENT_TYPES:
if content._size > int(settings.MAX_UPLOAD_SIZE):
raise forms.ValidationError(_(u'Please keep filesize under %s. Current filesize %s') % (filesizeformat(settings.MAX_UPLOAD_SIZE), filesizeformat(content._size)))
else:
raise forms.ValidationError(_(u'File type is not supported'))
return content
Есть только несколько улучшений:
Прежде всего, я обнаруживаю, что поле файла пуст (None) - без него Django выдаст исключение в веб-браузере.
Далее вызывается тип в int (settings.MAX_UPLOAD_SIZE), потому что это значение является строкой. Строки не могут использоваться для сравнения с числами.
И последнее, но не менее важное: префикс unicode 'u' в функции ValidationError.
Большое спасибо за этот фрагмент!
Ответ 6
Если кто-то ищет форму FileField
варианта решения @angelo, то здесь
from django import forms
from django.template.defaultfilters import filesizeformat
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
class RestrictedFileField(forms.FileField):
"""
Same as FileField, but you can specify:
* content_types - list containing allowed content_types. Example: ['application/pdf', 'image/jpeg']
* max_upload_size - a number indicating the maximum file size allowed for upload.
2.5MB - 2621440
5MB - 5242880
10MB - 10485760
20MB - 20971520
50MB - 5242880
100MB - 104857600
250MB - 214958080
500MB - 429916160
"""
def __init__(self, *args, **kwargs):
self.content_types = kwargs.pop("content_types")
self.max_upload_size = kwargs.pop("max_upload_size")
super(RestrictedFileField, self).__init__(*args, **kwargs)
def clean(self, data, initial=None):
file = super(RestrictedFileField, self).clean(data, initial)
try:
content_type = file.content_type
if content_type in self.content_types:
if file._size > self.max_upload_size:
raise ValidationError(_('Please keep filesize under %s. Current filesize %s') % (
filesizeformat(self.max_upload_size), filesizeformat(file._size)))
else:
raise ValidationError(_('Filetype not supported.'))
except AttributeError:
pass
return data
Затем создайте форму как
class ImageUploadForm(forms.Form):
"""Image upload form."""
db_image = RestrictedFileField(content_types=['image/png', 'image/jpeg'],
max_upload_size=5242880)
Ответ 7
Серверная сторона
Мой любимый метод проверки того, является ли файл слишком большим на стороне сервера, ifedapo olarewaju answer, используя валидатор.
Клиентская сторона
Проблема только с проверкой на стороне сервера заключается в том, что проверка выполняется только после завершения загрузки. Представьте себе, выгрузите огромный файл в ожидании веков, и только потом вам скажут, что файл слишком большой. Не было бы лучше, если бы браузер мог сообщить мне заранее, что файл слишком большой?
Ну, есть путь к этой стороне клиента, используя API-интерфейс HTML5!
Здесь необходим Javascript (в зависимости от JQuery):
$("form").submit(function() {
if (window.File && window.FileReader && window.FileList && window.Blob) {
var file = $('#id_file')[0].files[0];
if (file && file.size > 2 * 1024 * 1024) {
alert("File " + file.name + " of type " + file.type + " is too big");
return false;
}
}
});
Конечно, вам все равно нужна проверка на стороне сервера, защита от вредоносного ввода и пользователей, у которых нет Javascript.
Ответ 8
Другое элегантное решение с валидаторами, которое не жестко кодирует максимальный размер файла, - это использование валидатора на основе классов:
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator
from django.utils.translation import ugettext as _
class MaxSizeValidator(MaxValueValidator):
message = _('The file exceed the maximum size of %(limit_value)s MB.')
def __call__(self, value):
# get the file size as cleaned value
cleaned = self.clean(value.size)
params = {'limit_value': self.limit_value, 'show_value': cleaned, 'value': value}
if self.compare(cleaned, self.limit_value * 1024 * 1024): # convert limit_value from MB to Bytes
raise ValidationError(self.message, code=self.code, params=params)
а затем в вашей модели, например:
image = models.ImageField(verbose_name='Image', upload_to='images/', validators=[MaxSizeValidator(1)])
ОБНОВЛЕНИЕ: здесь - исходный код MaxValueValidator
для более подробной информации об этой работе.
Ответ 9
Я хочу поблагодарить всех людей, которые предоставили различные варианты решения этой проблемы. У меня были дополнительные требования, в которых я хотел (а) выполнить проверку длины файла в JavaScript перед отправкой, (б) выполнить вторую проверку защиты на сервере в forms.py
, (в) сохранить все жестко закодированные биты, включая конец -пользовательские сообщения в forms.py
, (d) я хотел, чтобы мой views.py
имел как можно меньше кода, связанного с файлами, и (d) загрузил информацию о файле в мою базу данных, поскольку это небольшие файлы, которые я хочу обслуживать только вошли в систему и мгновенно удаляют, когда удаляются элементы модели Meal
(т.е. просто отбросить их в /media/недостаточно).
Первая модель:
class Meal(models.Model) :
title = models.CharField(max_length=200)
text = models.TextField()
# Picture (you need content type to serve it properly)
picture = models.BinaryField(null=True, editable=True)
content_type = models.CharField(max_length=256, null=True, help_text='The MIMEType of the file')
# Shows up in the admin list
def __str__(self):
return self.title
Затем вам нужна форма, которая выполняет как проверку на сервере, так и преобразование перед сохранением из InMemoryUploadedFile
в bytes
и захватывает Content-Type
для дальнейшего обслуживания.
class CreateForm(forms.ModelForm):
max_upload_limit = 2 * 1024 * 1024
max_upload_limit_text = str(max_upload_limit) # A more natural size would be nice
upload_field_name = 'picture'
# Call this 'picture' so it gets copied from the form to the in-memory model
picture = forms.FileField(required=False, label='File to Upload <= '+max_upload_limit_text)
class Meta:
model = Meal
fields = ['title', 'text', 'picture']
def clean(self) : # Reject if the file is too large
cleaned_data = super().clean()
pic = cleaned_data.get('picture')
if pic is None : return
if len(pic) > self.max_upload_limit:
self.add_error('picture', "File must be < "+self.max_upload_limit_text+" bytes")
def save(self, commit=True) : # Convert uploaded files to bytes
instance = super(CreateForm, self).save(commit=False)
f = instance.picture # Make a copy
if isinstance(f, InMemoryUploadedFile):
bytearr = f.read();
instance.content_type = f.content_type
instance.picture = bytearr # Overwrite with the actual image data
if commit:
instance.save()
return instance
В шаблон добавьте этот код (адаптированный из предыдущего ответа):
<script>
$("#upload_form").submit(function() {
if (window.File && window.FileReader && window.FileList && window.Blob) {
var file = $('#id_{{ form.upload_field_name }}')[0].files[0];
if (file && file.size > {{ form.max_upload_limit }} ) {
alert("File " + file.name + " of type " + file.type + " must be < {{ form.max_upload_limit_text }}");
return false;
}
}
});
</script>
Вот код представления, который обрабатывает и создание, и обновление:
class MealFormView(LoginRequiredMixin, View):
template = 'meal_form.html'
success_url = reverse_lazy('meals')
def get(self, request, pk=None) :
if not pk :
form = CreateForm()
else:
meal = get_object_or_404(Meal, id=pk, owner=self.request.user)
form = CreateForm(instance=meal)
ctx = { 'form': form }
return render(request, self.template, ctx)
def post(self, request, pk=None) :
if not pk:
form = CreateForm(request.POST, request.FILES or None)
else:
meal = get_object_or_404(Meal, id=pk, owner=self.request.user)
form = CreateForm(request.POST, request.FILES or None, instance=meal)
if not form.is_valid() :
ctx = {'form' : form}
return render(request, self.template, ctx)
form.save()
return redirect(self.success_url)
Это очень простое представление, которое гарантирует, что request.FILES передается в при создании экземпляра. Вы могли бы почти использовать универсальный CreateView, если бы он (а) использовал мою форму и (б) передавал request.files при создании экземпляра модели.
Просто для завершения работы у меня есть следующее простое представление для потоковой передачи файла:
def stream_file(request, pk) :
meal = get_object_or_404(Meal, id=pk)
response = HttpResponse()
response['Content-Type'] = meal.content_type
response['Content-Length'] = len(meal.picture)
response.write(meal.picture)
return response
Это не заставляет пользователей входить в систему, но я пропустил это, поскольку этот ответ уже слишком длинный.
Ответ 10
from django.forms.utils import ErrorList
class Mymodelform(forms.ModelForm):
class Meta:
model = Mymodel
fields = '__all__'
def clean(self):image = self.cleaned_data.get('image')
# 5MB - 5242880
if org_image._size > 5242880:
self._errors["image"] = ErrorList([u"Image too heavy."])
Ответ 11
Вы можете расширить Django MaxValueValidator
и перезаписать его clean()
, чтобы получить размер файла:
from django.core.validators import MaxValueValidator
from django.utils.deconstruct import deconstructible
from django.utils.translation import ugettext_lazy as _
@deconstructible
class MaxKibFileSizeValidator(MaxValueValidator):
message = _('File size %(show_value)d KiB exceeds maximum file size of %(limit_value)d KiB.')
def clean(self, filefield) -> float:
return filefield.file.size / 1024
Ответ 12
В моем случае django ограничивает размер загружаемого файла. Добавьте следующие настройки, чтобы снять ограничение.
# allow upload big file
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 15 # 15M
FILE_UPLOAD_MAX_MEMORY_SIZE = DATA_UPLOAD_MAX_MEMORY_SIZE