Django Ограничение размера загрузки файла - PullRequest
54 голосов
/ 18 марта 2010

В моем приложении django есть форма, куда пользователи могут загружать файлы.
Как установить ограничение на размер загружаемого файла, чтобы, если пользователь загружает файл, размер которого превышает мой лимит, форма не будет действительной и выдает ошибку?

Ответы [ 11 ]

65 голосов
/ 26 января 2012

Вы можете использовать этот фрагмент формата проверки. Что он делает, это

  • позволяет указать, какие форматы файлов могут быть загружены.

  • и позволяет установить ограничение размера файла для загрузки.

Во-первых. Создайте файл с именем formatChecker.py внутри приложения, где у вас есть модель с FileField, которую вы хотите принять для определенного типа файлов.

Это ваш формат Checker.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».

* * 1 022 Пример: * 1 023 *
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' на типы файлов, которые вы хотите принять.

43 голосов
/ 18 марта 2010

Этот код может помочь:

# 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 - проверка по типу и размеру содержимого файла

37 голосов
/ 10 февраля 2016

другое решение использует валидаторы

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.')

тогда в вашей форме с полем File у вас будет что-то вроде этого

image = forms.FileField(required=False, validators=[file_size])
19 голосов
/ 18 марта 2010

Я считаю, что форма django получает файл только после того, как он был полностью загружен. Поэтому, если кто-то загрузит файл 2 ГБ, вам гораздо лучше с веб-сервером проверять размер на лету.

См. почтовую ветку для получения дополнительной информации.

9 голосов
/ 27 июня 2012

Просто короткая заметка о фрагменте, который был включен в эту ветку:

Посмотрите на этот фрагмент: 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

Есть только несколько улучшений:

Прежде всего, я определяю, является ли поле файла пустым (Нет) - без него Django будет вызывать исключение в веб-браузере.

Далее идет приведение типов в int (settings.MAX_UPLOAD_SIZE), потому что это значение настройки является строкой. Строки нельзя использовать для сравнения с числами.

И последнее, но не менее важное: префикс unicode 'u' в функции ValidationError.

Большое спасибо за этот фрагмент!

8 голосов
/ 12 августа 2016

Серверная сторона

Мой любимый метод проверки, является ли файл слишком большим на стороне сервера - это ifedapo olarewaju ответ с использованием валидатора.

Клиентская сторона

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

Ну, есть путь к этой клиентской стороне , используя HTML5 File API !

Вот необходимый 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 голосов
/ 27 августа 2014

Если кто-то ищет вариант 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)
3 голосов
/ 09 февраля 2019

Другое элегантное решение с валидаторами, которое не кодирует максимальный размер файла, - это использование валидатора на основе классов:

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 для получения дополнительной информации об этой работе.

2 голосов
/ 26 марта 2019

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

Это не заставляет пользователей входить в систему, но я пропустил это, поскольку этот ответ уже слишком длинный.

0 голосов
/ 01 марта 2019

Вы можете расширить MaxValueValidator в Django и переписать его 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...