如何使用魔法来验证 Django 表单清理方法中的文件类型?
Posted
技术标签:
【中文标题】如何使用魔法来验证 Django 表单清理方法中的文件类型?【英文标题】:How does one use magic to verify file type in a Django form clean method? 【发布时间】:2012-01-28 15:55:53 【问题描述】:我在 Django 中使用 FileField 编写了一个电子邮件表单类。我想通过检查其 mimetype 来检查上传文件的类型。随后,我想将文件类型限制为 pdf、word 和打开的 office 文档。
为此,我已经安装了 python-magic 并希望按照 python-magic 的规范检查文件类型如下:
mime = magic.Magic(mime=True)
file_mime_type = mime.from_file('address/of/file.txt')
但是,最近上传的文件在我的服务器上缺少地址。我也不知道任何类似于“from_file_content”的 mime 对象的方法在给定文件内容的情况下检查 mime 类型。
使用magic验证Django表单中上传文件的文件类型的有效方法是什么?
【问题讨论】:
【参考方案1】:Stan 用缓冲区描述了很好的变体。不幸的是,这种方法的弱点是将文件读取到内存中。另一种选择是使用临时存储文件:
import tempfile
import magic
with tempfile.NamedTemporaryFile() as tmp:
for chunk in form.cleaned_data['file'].chunks():
tmp.write(chunk)
print(magic.from_file(tmp.name, mime=True))
另外,您可能需要检查文件大小:
if form.cleaned_data['file'].size < ...:
print(magic.from_buffer(form.cleaned_data['file'].read()))
else:
# store to disk (the code above)
Additionally:
在命名的临时文件仍处于打开状态时,该名称是否可用于第二次打开文件,因平台而异(在 Unix 上可以这样使用;在 Windows NT 或更高版本上不能)。
所以你可能想像so一样处理它:
import os
tmp = tempfile.NamedTemporaryFile(delete=False)
try:
for chunk in form.cleaned_data['file'].chunks():
tmp.write(chunk)
print(magic.from_file(tmp.name, mime=True))
finally:
os.unlink(tmp.name)
tmp.close()
另外,你可能想在read()
之后seek(0)
:
if hasattr(f, 'seek') and callable(f.seek):
f.seek(0)
Where uploaded data is stored
【讨论】:
谢谢,当我尝试使用cleaned_data 时,Django 注意到文件/tmp/filename.doc 是未定义的。你知道为什么吗? 以某种方式或另一种方式,您的文件将被加载到内存中。而且我更喜欢避免直接使用临时路径。 @Stan 当服务器因内存不足而崩溃时,您是否遇到过问题?我认为这就是程序员试图避免将文件读入内存的原因,这就是为什么 djangoUploadedFile
有 chunks()
方法
@AlexeySavanovich : m.from_buffer(request.FILES['my_file_field'].multiple_chunks())
在这种情况下应该可以工作。
非常感谢!事实上,multiple_chunks()
似乎可以做到这一点,而无需一次上传整个文件进行验证。【参考方案2】:
为什么不在你的观点中尝试这样的事情:
m = magic.Magic()
m.from_buffer(request.FILES['my_file_field'].read())
如果django.forms.Form
真的不行,则使用request.FILES
代替form.cleaned_data
。
【讨论】:
第二个代码不正确。multiple_chunks()
not 返回块,它返回一个布尔值:文件是否大到可以分割成块。 docs.djangoproject.com/en/1.5/topics/http/file-uploads/…
from_buffer
需要一个字符串缓冲区,而不是一个迭代器。 AFAIK 您的新代码将失败并出现 AttributeError,因为迭代器没有 len()
。除了手动获取第一个块之外,我在这里没有看到任何好的解决方案。【参考方案3】:
mime = magic.Magic(mime=True)
attachment = form.cleaned_data['attachment']
if hasattr(attachment, 'temporary_file_path'):
# file is temporary on the disk, so we can get full path of it.
mime_type = mime.from_file(attachment.temporary_file_path())
else:
# file is on the memory
mime_type = mime.from_buffer(attachment.read())
另外,你可能想在read()
之后seek(0)
:
if hasattr(f, 'seek') and callable(f.seek):
f.seek(0)
来自Django code 的示例。在验证期间对图像字段执行。
【讨论】:
【参考方案4】:您可以使用django-safe-filefield 包来验证上传的文件扩展名是否匹配它的 MIME 类型。
from safe_filefield.forms import SafeFileField
class MyForm(forms.Form):
attachment = SafeFileField(
allowed_extensions=('xls', 'xlsx', 'csv')
)
【讨论】:
【参考方案5】:如果您正在处理文件上传并且只关心图片,
Django 会为你设置content_type
(或者更确切地说是为它自己?):
from django.forms import ModelForm
from django.core.files import File
from django.db import models
class MyPhoto(models.Model):
photo = models.ImageField(upload_to=photo_upload_to, max_length=1000)
class MyForm(ModelForm):
class Meta:
model = MyPhoto
fields = ['photo']
photo = MyPhoto.objects.first()
photo = File(open('1.jpeg', 'rb'))
form = MyForm(files='photo': photo)
if form.is_valid():
print(form.instance.photo.file.content_type)
它不依赖于用户提供的内容类型。但
django.db.models.fields.files.FieldFile.file
是一个无证
property.
其实最初content_type
是从request设置的,但是当
表单得到验证,值为updated。
关于非图像, 做request.FILES['name'].read()
对我来说似乎没问题。
首先,这就是Django 所做的。二、默认大于2.5Mb的文件
在disk 上是stored。所以让我指出other 的答案
在这里。
为了好奇,这里是导致更新的堆栈跟踪
content_type
:
django.forms.forms.BaseForm.is_valid:self.errors django.forms.forms.BaseForm.errors:self.full_clean() django.forms.forms.BaseForm.full_clean: self._clean_fields() django.forms.forms.BaseForm._clean_fiels:field.clean() django.forms.fields.FileField.clean:super().clean() django.forms.fields.Field.clean:self.to_python() django.forms.fields.ImageField.to_python
【讨论】:
以上是关于如何使用魔法来验证 Django 表单清理方法中的文件类型?的主要内容,如果未能解决你的问题,请参考以下文章