仅在服务器端的FileField中接受某种文件类型

mar*_*oxe 58 django file-upload

我如何限制FileField只能以优雅的方式接受某种类型的文件(视频,音频,PDF格式等),服务器端?

Sae*_*aeX 76

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

在你的应用程序中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(u'Unsupported file extension.')
Run Code Online (Sandbox Code Playgroud)

然后在你的models.py:

from .validators import validate_file_extension
Run Code Online (Sandbox Code Playgroud)

...并在表单字段中使用验证器:

class Document(models.Model):
    file = models.FileField(upload_to="documents/%Y/%m/%d", validators=[validate_file_extension])
Run Code Online (Sandbox Code Playgroud)

另请参阅:如何使用FileField限制ModelForms的文件上载文件类型?.

  • @dabad只使用扩展名不利于文件验证,这些答案会造成安全漏洞; 请看到这些答案的人检查CVE与不同的格式和验证器相关,以枕头/ PIL为例:O (3认同)

ill*_*nan 53

版本1.11中的Django 有一个新添加FileExtensionValidator的模型字段,文档在这里:https://docs.djangoproject.com/en/dev/ref/validators/#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'])])
Run Code Online (Sandbox Code Playgroud)

请注意,此方法不安全.来自Django文档的引文:

不要依赖文件扩展名的验证来确定文件的类型.无论文件包含哪些数据,都可以重命名文件以获得任何扩展名.

还有新的validate_image_file_extension(https://docs.djangoproject.com/en/dev/ref/validators/#validate-image-file-extension)用于验证图像扩展(使用Pillow).

  • 文件已上传,即使由于某种原因未将其添加到“allowed_extensions”中。使用 Django Rest 框架。 (3认同)
  • @VineethSai 我想您还需要在序列化器中设置验证器,而不仅仅是在模型中。 (2认同)

Thi*_*ers 13

一些人建议使用python-magic来验证文件实际上是您期望接收的类型。这可以合并到validator已接受答案中的建议中:

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.')
Run Code Online (Sandbox Code Playgroud)

此示例仅验证 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,))
Run Code Online (Sandbox Code Playgroud)

  • 绝对是页面上最安全的答案! (5认同)

Dom*_*ger 10

有一个Django片段可以做到这一点:

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()
Run Code Online (Sandbox Code Playgroud)

  • 试试这个链接:http://nemesisdesign.net/blog/coding/django-filefield-content-type-size-validation/ (3认同)
  • 这是一个基于扩展的过滤器,根本不可靠.我正在考虑在上传完成后分析文件. (2认同)
  • 您可以使用python-magic(libmagic的包装器)来获取mimetype,然后基于此接受/拒绝. (2认同)

小智 7

第一.在应用程序内创建一个名为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")

    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
Run Code Online (Sandbox Code Playgroud)

第二.在models.py中,添加以下内容:

from formatChecker import ContentTypeRestrictedFileField
Run Code Online (Sandbox Code Playgroud)

然后使用'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)
Run Code Online (Sandbox Code Playgroud)

当您只想接受FileField中的某种文件类型时,这些是您必须要做的事情.

  • 在超级调用之前添加此行:self.widget = ClearableFileInput(attrs = {'accept':','.join(self.content_types)})将仅在模式窗口中选择可接受的内容类型. (3认同)

小智 5

您可以使用以下内容来限制表单中的文件类型

file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))
Run Code Online (Sandbox Code Playgroud)


Oğu*_*han 5

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

https://docs.djangoproject.com/en/3.0/ref/validators/#fileextensionvalidator

警告

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