【问题标题】:How does one use magic to verify file type in a Django form clean method?如何使用魔法来验证 Django 表单清理方法中的文件类型?
【发布时间】: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表单中上传文件的文件类型的有效方法是什么?

【问题讨论】:

    标签: django django-forms django-file-upload python-magic


    【解决方案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 当服务器因内存不足而崩溃时,您是否遇到过问题?我认为这就是程序员试图避免将文件读入内存的原因,这就是为什么 django UploadedFilechunks() 方法
    • @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

        【讨论】:

          猜你喜欢
          • 2019-06-21
          • 1970-01-01
          • 2012-02-22
          • 2020-06-22
          • 1970-01-01
          • 2018-05-10
          • 2023-03-12
          • 2012-07-19
          • 1970-01-01
          相关资源
          最近更新 更多