Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django 仅接受服务器端文件字段中的特定文件类型_Django_File Upload - Fatal编程技术网

Django 仅接受服务器端文件字段中的特定文件类型

Django 仅接受服务器端文件字段中的特定文件类型,django,file-upload,Django,File Upload,如何限制FileField仅在服务器端以优雅的方式接受特定类型的文件(视频、音频、pdf等)?有一种方法可以做到这一点: import os from django import forms class ExtFileField(forms.FileField): """ Same as forms.FileField, but you can specify a file extension whitelist. >>> from django.

如何限制
FileField
仅在服务器端以优雅的方式接受特定类型的文件(视频、音频、pdf等)?

有一种方法可以做到这一点:

import os

from django import forms

class ExtFileField(forms.FileField):
    """
    Same as forms.FileField, but you can specify a file extension whitelist.

    >>> from django.core.files.uploadedfile import SimpleUploadedFile
    >>>
    >>> t = ExtFileField(ext_whitelist=(".pdf", ".txt"))
    >>>
    >>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content'))
    >>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content'))
    >>>
    >>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content'))
    Traceback (most recent call last):
    ...
    ValidationError: [u'Not allowed filetype!']
    """
    def __init__(self, *args, **kwargs):
        ext_whitelist = kwargs.pop("ext_whitelist")
        self.ext_whitelist = [i.lower() for i in ext_whitelist]

        super(ExtFileField, self).__init__(*args, **kwargs)

    def clean(self, *args, **kwargs):
        data = super(ExtFileField, self).clean(*args, **kwargs)
        filename = data.name
        ext = os.path.splitext(filename)[1]
        ext = ext.lower()
        if ext not in self.ext_whitelist:
            raise forms.ValidationError("Not allowed filetype!")

#-------------------------------------------------------------------------

if __name__ == "__main__":
    import doctest, datetime
    doctest.testmod()

首先。在应用程序中创建一个名为formatChecker.py的文件,该应用程序中的模型包含要接受特定文件类型的文件字段

这是您的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
第二。在您的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)

当您只想在FileField中接受某个文件类型时,必须执行这些操作

我认为你最适合使用多米尼克·罗杰在回答中指定的方法,而丹尼尔·奎因提到的方法是最好的方法。如果有人足够聪明,可以更改扩展名,至少你会用标题捕捉到他们。

一个非常简单的方法是使用自定义验证器

在应用程序的
validators.py
中:

def validate_file_extension(value):
    import os
    from django.core.exceptions import ValidationError
    ext = os.path.splitext(value.name)[1]  # [0] returns path+filename
    valid_extensions = ['.pdf', '.doc', '.docx', '.jpg', '.png', '.xlsx', '.xls']
    if not ext.lower() in valid_extensions:
        raise ValidationError('Unsupported file extension.')
from .validators import validate_file_extension
然后在
models.py中:

def validate_file_extension(value):
    import os
    from django.core.exceptions import ValidationError
    ext = os.path.splitext(value.name)[1]  # [0] returns path+filename
    valid_extensions = ['.pdf', '.doc', '.docx', '.jpg', '.png', '.xlsx', '.xls']
    if not ext.lower() in valid_extensions:
        raise ValidationError('Unsupported file extension.')
from .validators import validate_file_extension
。。。并使用表单字段的验证程序:

class Document(models.Model):
    file = models.FileField(upload_to="documents/%Y/%m/%d", validators=[validate_file_extension])
另见:

警告

用于保护代码执行环境免受恶意媒体文件的攻击

  • 使用Exif正确验证媒体文件
  • 将媒体文件与应用程序代码分开 执行环境
  • 如果可能,使用S3、GCS、Minio或 类似的吗
  • 在客户端加载媒体文件时,请使用客户端本机方法(例如,如果在客户端中不安全地加载媒体文件) 浏览器,可能会导致执行“精心编制”的JavaScript代码)

  • 此外,我将通过一些额外的行为来扩展这个类

    class ContentTypeRestrictedFileField(forms.FileField):
        ...
        widget = None
        ...
        def __init__(self, *args, **kwargs):
            ...
            self.widget = forms.ClearableFileInput(attrs={'accept':kwargs.pop('accept', None)})
            super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs)
    

    当我们使用param accept=“.pdf,.txt”创建实例时,在默认文件结构的弹出窗口中,我们将看到扩展名已传递的文件。

    您可以在设置中定义一个已接受mime类型的列表,然后定义一个验证器,该验证器使用python magic检测mime类型,如果mime类型未被接受,则引发ValidationError。在文件表单字段上设置该验证器


    唯一的问题是,有时mime类型是application/octet-stream,它可能对应于不同的文件格式。你们中有人解决了这个问题吗?

    Django在版本
    1.11
    中有一个新添加的
    FileExtensionValidator
    用于模型字段,文档如下:

    有关如何验证文件扩展名的示例:

    from django.core.validators import FileExtensionValidator
    from django.db import models
    
    class MyModel(models.Model):
        pdf_file = models.FileField(upload_to='foo/',
                                    validators=[FileExtensionValidator(allowed_extensions=['pdf'])])
    
    请注意,此方法不安全。引用Django文件:

    不要依赖文件扩展名的验证来确定文件的扩展名 类型。无论数据是什么,文件都可以重命名为具有任何扩展名 它们包含


    还有一个新的
    validate\u image\u file\u extension
    ()用于验证图像扩展名(使用枕头)。

    您可以使用下面的命令限制表单中的文件类型

    file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))
    

    一些人建议使用来验证文件是否为您希望接收的类型。可将其纳入已接受答案中建议的
    验证器

    import os
    import magic
    from django.core.exceptions import ValidationError
    
    def validate_is_pdf(file):
        valid_mime_types = ['application/pdf']
        file_mime_type = magic.from_buffer(file.read(1024), mime=True)
        if file_mime_type not in valid_mime_types:
            raise ValidationError('Unsupported file type.')
        valid_file_extensions = ['.pdf']
        ext = os.path.splitext(file.name)[1]
        if ext.lower() not in valid_file_extensions:
            raise ValidationError('Unacceptable file extension.')
    
    此示例仅验证pdf,但可以将任意数量的mime类型和文件扩展名添加到数组中

    假设您将上述内容保存在
    validators.py
    中,您可以将其合并到您的模型中,如下所示:

    from myapp.validators import validate_is_pdf
    
    class PdfFile(models.Model):
        file = models.FileField(upload_to='pdfs/', validators=(validate_is_pdf,))
    

    在检查了接受的答案后,我决定分享一个基于Django文档的技巧。已经有一个用于验证文件扩展名的验证器。您不需要重写自己的自定义函数来验证是否允许文件扩展名

    警告

    不要依赖文件扩展名的验证来确定文件的扩展名 类型。无论数据是什么,文件都可以重命名为具有任何扩展名 它们包含


    这是一个基于扩展的过滤器,根本不可靠。我正在考虑上传完后分析文件。@maroxe你找到解决方法了吗?我只是在确定文件是否为音频文件时遇到了同样的问题。请尝试以下链接:@user126795-这仍然不能确定它是正确的类型-它似乎只是依赖于指定的内容类型-这可能不可靠。您可以使用python magic(libmagic的包装器)要获取mimetype,然后根据它接受/拒绝。content-type很容易被欺骗。在超级调用之前添加这一行:self.widget=ClearableFileInput(attrs={'accept':','.join(self.content_types)}将只在模式窗口中选择接受的内容类型。@laffuste对您的评论进行了投票,这在Mac上似乎不起作用(即,任何文件都是可选的)。还没有在Windows下测试过。在chrome上工作,FF没有给出****。我发现无论如何,按扩展过滤比按内容类型过滤要好。后者几乎是由您的浏览器+操作系统任意设置的。@dabad仅使用扩展不利于文件验证,这种回答会造成安全漏洞;请看到这些答案的人也检查与不同格式和验证器相关的CVE,以枕头/PIL为例:O@RenjithThankachan即使有时候图像exif数据也可以用任意代码修改,但这一技巧在过去用于PHP应用程序。我们可以做的一件事是使用S3之类的解决方案,Minio将用户的媒体文件从应用程序代码执行环境@ammadkhalid中分离出来,以打开对话框,将文件限制在客户端的某些类型。既然您已经确定这是最好的方法,为什么不发布一些代码向我们其他人演示呢!?文件已上载,即使出于某种原因未将其添加到
    允许的\u扩展名中。使用Django Rest框架。绝对是页面上最安全的答案!这适用于表单,但不验证模型