在Django中按内容类型验证文件的更好方法

1 投票
1 回答
2821 浏览
提问于 2025-04-18 13:31

我在下面实现了一个代码片段:

https://djangosnippets.org/snippets/1303/

这是我目前的代码:

models.py

class Vehicle(models.Model):
    pub_date = models.DateTimeField('Date Published', auto_now_add=True)
    make = models.CharField(max_length=200)
    model = models.CharField(max_length=200)
    picture = models.FileField(upload_to='picture')

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('recipe_edit', kwargs={'pk': self.pk})

views.py

def vehicle_list(request, template_name='vehicle/vehicle_list.html'):
    if request.POST:
        form = VehicleForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('vehicle_list')
    else:
        form = VehicleForm()  # Create empty form

    vehicles = Vehicle.objects.all()  # Retrieve all vehicles from DB
    return render(request, template_name, {
        'vehicles': vehicles,
        'form': form
    },context_instance=RequestContext(request))

forms.py

class VehicleForm(forms.ModelForm):
    class Meta:
        model = Vehicle

    def clean_picture(self):
        content = self.cleaned_data['picture']
        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 file size under %s', filesizeformat(content.size))
        else:
            raise forms.ValidationError('File type is not supported')

根据我的理解,这种方法可以通过修改头部轻松覆盖。我想问的是,对于这种情况,有没有更好的解决办法?

1 个回答

1

为了确认给定的文件内容和客户端提供的内容类型是否匹配,你需要一个完整的数据库,这个数据库可以帮助你识别内容类型。

不过,你可以依赖libmagic这个项目。这个库在pypi上有相关的绑定,可以通过这个链接找到:python-magic

你需要调整你的VehicleForm,使其能够检测内容类型:

class VehicleForm(forms.ModelForm):

    class Meta(object):
        model = Vehicle

    def clean_picture(self):
        content = self.cleaned_data['picture']
        try:
            content.open()
            # read only a small chunk or a large file could nuke the server
            file_content_type = magic.from_buffer(content.read(32768),
                                                  mime=True)
        finally:
            content.close()

        client_content_root_type = content.content_type.split('/')[0]
        file_content_root_type = file_content_type.split('/')[0]

        if client_content_root_type in settings.CONTENT_TYPES and \
                file_content_root_type in settings.CONTENT_TYPES:
            if content.size > settings.MAX_UPLOAD_SIZE:
                raise forms.ValidationError('Please keep file size under %s',
                                            filesizeformat(content.size))
        else:
            raise forms.ValidationError('File type is not supported')
        return content

这段代码是为了展示如何工作,而不是为了减少冗余代码。

我不建议你在生产环境中自己这样做。我建议使用已经存在的代码。如果你只是想确认是否上传了图片并且可以查看,建议使用ImageField这个表单字段。请注意,ImageField使用Pillow库来确保图片可以被打开。这可能会对你的服务器造成威胁,也可能不会。

还有其他几个项目可以实现确保上传的文件是特定内容类型的功能。

撰写回答