我在Django应用中有一个表单,用户可以在其中上传文件。
我如何设置上传文件大小的限制,以便如果用户上传的文件大于我的限制,则该表格将无效并且会引发错误?
我在Django应用中有一个表单,用户可以在其中上传文件。
我如何设置上传文件大小的限制,以便如果用户上传的文件大于我的限制,则该表格将无效并且会引发错误?
Answers:
此代码可能会帮助:
# 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
MiB
总是总是1048576字节,它不是十的幂。至于MB
,这是不明确的,如果您遵循IEC标准,则可能是1000000字节,如果使用Windows等,则可能是1048576字节。您链接到的Wikipedia文章就是证明。
content.size
(无下划线)
MAX_UPLOAD_SIZE
为字符串。它应该是一个数字-此代码将允许任何大小的上传,因为无法达到第一个ValidationError。
您可以使用此代码段格式检查器。它的作用是
它允许您指定允许上传的文件格式。
并可以设置要上传文件的文件大小限制。
第一。在应用程序内创建一个名为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", 0)
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
然后,使用此“ 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)
您可以将“ max_upload_size”的值更改为所需的文件大小限制。您也可以将“ content_types”列表中的值更改为要接受的文件类型。
kwargs.pop
默认值。更改为此self.content_types = kwargs.pop("content_types", []) self.max_upload_size = kwargs.pop("max_upload_size", [])
另一个解决方案是使用验证器
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])
_size
而这个变量没有。
我相信django表单只有在完全上传后才能接收文件,这就是为什么如果有人上传2Gb文件,那么通过网络服务器即时检查大小会更好。
有关更多信息,请参见此邮件线程。
简短介绍一下此线程中包含的代码段:
这非常有用,但是其中包括一些小错误。更健壮的代码应如下所示:
# 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)类型转换,因为该设置值是一个字符串。字符串不能用于与数字进行比较。
最后但并非最不重要的一点是ValidationError函数中的unicode'u'前缀。
非常感谢您提供这段代码!
'.pdf'
而不是content_type
检查。同时更简单,更强大。
如果有人正在寻找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)
我最喜欢的检查文件是否在服务器端太大的方法是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的用户。
我要感谢所有为这个问题提供各种不同解决方案的人们。我还有其他要求,我想要(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
然后,您需要一种既可以进行服务器内验证也可以进行从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。如果(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
这不会强制用户登录,但由于此答案已经太长,我将其省略。
就我而言,django限制了上传文件的大小。添加以下设置将消除限制。
# allow upload big file
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 15 # 15M
FILE_UPLOAD_MAX_MEMORY_SIZE = DATA_UPLOAD_MAX_MEMORY_SIZE
FILE_UPLOAD_MAX_MEMORY_SIZE
需要的是一样的吗?文档说它将从此限制开始流式传输到文件系统...因此...仅DATA_UPLOAD_MAX_MEMORY_SIZE
限制最大上传文件大小。
使用验证器的另一个不错的解决方案是不使用硬编码最大文件大小,这是使用基于类的验证器:
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
此作品更多详细信息的源代码。
您可以扩展DjangoMaxValueValidator
并覆盖它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