Django文件上传大小限制

dan*_*els 60 django file-upload

我的django应用程序中有一个表单,用户可以在其中上传文件.
如何设置上传文件大小的限制,以便如果用户上传的文件大于我的限制,表单将无效并且会引发错误?

小智 68

您可以使用此代码段格式检查器.它的作用是什么

  • 它允许您指定允许上载的文件格式.

  • 并允许您设置要上载的文件的文件大小限制.

第一.在应用程序内创建一个名为formatChecker.py的文件,其中包含具有要接受某种文件类型的FileField的模型.

这是你的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
Run Code Online (Sandbox Code Playgroud)

第二.在models.py中,添加以下内容:

from formatChecker import ContentTypeRestrictedFileField
Run Code Online (Sandbox Code Playgroud)

然后使用'ContentTypeRestrictedFileField'代替使用'FileField'.

例:

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)
Run Code Online (Sandbox Code Playgroud)

您可以将"max_upload_size"的值更改为所需的文件大小限制.您还可以将"content_types"列表中的值更改为您要接受的文件类型.

  • 多么被低估的答案!比验证的更完整,略胜一筹. (5认同)
  • 第23行,在__init__中self.content_types = kwargs.pop("content_types")KeyError:'content_types' - 一直给我这个错误 (5认同)
  • 给出错误__init __()在创建数据库时得到了一个意外的关键字参数content_types (4认同)
  • 这真的应该建立在Django中. (2认同)
  • 遇到此问题的人应该记住文档“ kwargs.pop”应该具有默认值。更改为此`self.content_types = kwargs.pop(“ content_types”,[])self.max_upload_size = kwargs.pop(“ max_upload_size”,[])` (2认同)

Nac*_*cho 48

此代码可能有所帮助:

# 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
Run Code Online (Sandbox Code Playgroud)

取自:Django Snippets - 根据文件内容类型和大小进行验证

  • 在Django 1.10中使用`content.size`(无下划线) (6认同)
  • 不要将`MAX_UPLOAD_SIZE`设置为字符串.它应该是一个数字 - 此代码将允许任何大小上载,因为无法访问第一个ValidationError. (3认同)
  • 可以肯定的是,在“ 5242880”的末尾您输了0。应为“ 52428800” (2认同)

ife*_*aju 44

另一种解决方案是使用验证器

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.')
Run Code Online (Sandbox Code Playgroud)

然后在你的表单与文件字段,你有这样的东西

image = forms.FileField(required=False, validators=[file_size])
Run Code Online (Sandbox Code Playgroud)

  • 这是如此优雅,你应该使用西装和领带编写这个代码. (4认同)
  • 这是我最喜欢的,因为其他人访问私有变量 `_size` 而这个没有。 (3认同)
  • 小心!只有在使用表单保存数据时才会调用验证,否则您必须手动调用验证,例如.保存到db之前'instance.full_clean()'. (3认同)
  • @Hemant_Negi 我相信这个问题确实表明该文件是通过表单接收的,所以不用担心。 (2认同)

Dmi*_*nko 20

我相信django表单只有在完全上传后才会收到文件.这就是为什么如果有人上传2Gb文件,那么你可以通过web-server检查大小的文件来实现更好的效果.

有关详细信息,请参阅此邮件主题.


小智 9

关于此主题中包含的片段的简短说明:

看一下这个片段: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
Run Code Online (Sandbox Code Playgroud)

只有一些改进:

首先,我正在检测文件字段是否为空(无) - 如果没有它,Django将在Web浏览器中转换异常.

接下来是int(settings.MAX_UPLOAD_SIZE)中的类型转换,因为该设置值是一个字符串.字符串不能用于与数字进行比较.

最后但并非最不重要的是,ValidationError函数中的unicode'u'前缀.

非常感谢你的这个片段!


Hem*_*egi 8

如果有人正在寻找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
Run Code Online (Sandbox Code Playgroud)

然后创建一个表单

class ImageUploadForm(forms.Form):
    """Image upload form."""
    db_image = RestrictedFileField(content_types=['image/png', 'image/jpeg'],
                                   max_upload_size=5242880)
Run Code Online (Sandbox Code Playgroud)


Fli*_*imm 8

服务器端

我最喜欢检查文件服务器端是否太大的方法是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;
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

当然,您仍然需要服务器端验证,以防止恶意输入,以及未启用Javascript的用户.


Gia*_*olo 6

验证器的另一个优雅的解决方案是使用基于类的验证器,该解决方案不会对最大文件大小进行硬编码:

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)
Run Code Online (Sandbox Code Playgroud)

然后,在您的模型中,例如:

image = models.ImageField(verbose_name='Image', upload_to='images/', validators=[MaxSizeValidator(1)])
Run Code Online (Sandbox Code Playgroud)

编辑:MaxValueValidator是有关此作品的更多详细信息的源代码。


drc*_*uck 5

我要感谢所有为这个问题提供各种不同解决方案的人。我还有其他要求,我想 (a) 在提交之前在 JavaScript 中进行文件长度验证,(b) 在服务器内进行第二道防线验证forms.py,(c) 保留所有硬编码位,包括最终用户消息在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
Run Code Online (Sandbox Code Playgroud)

然后,您需要一个表单,既可以进行服务器内验证,也可以进行预保存转换InMemoryUploadedFilebytes并获取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
Run Code Online (Sandbox Code Playgroud)

在模板中,添加以下代码(改编自之前的答案):

<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>
Run Code Online (Sandbox Code Playgroud)

最近我尽可能远离 JQuery,所以上面在 Vanilla JS 中检查文件大小的客户端代码是:

<script>
  document.getElementById("upload_form").addEventListener("submit", function() {
    console.log('Checking file size');
    if (window.File && window.FileReader && window.FileList && window.Blob) {
        var file = document.getElementById('id_{{ form.upload_field_name }}').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>
Run Code Online (Sandbox Code Playgroud)

以下是处理创建和更新的视图代码:

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)
Run Code Online (Sandbox Code Playgroud)

这是一个非常简单的视图,可确保在创建实例期间传入 request.FILES。如果在创建模型实例时 (a) 使用我的表单并 (b) 传递 request.files,那么您几乎可以使用通用 CreateView。

为了完成这项工作,我有以下简单的视图来流式传输文件:

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
Run Code Online (Sandbox Code Playgroud)

这不会强制用户登录,但我省略了这一点,因为这个答案已经太长了。


wea*_*ing 5

就我而言,django 限制了上传文件的大小。添加以下设置将取消限制。

# allow upload big file
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 15  # 15M
FILE_UPLOAD_MAX_MEMORY_SIZE = DATA_UPLOAD_MAX_MEMORY_SIZE
Run Code Online (Sandbox Code Playgroud)