Принимать только определенный тип файлов в FileField на стороне сервера - PullRequest
53 голосов
/ 06 сентября 2010

Как можно ограничить FileField только элегантным приемом файлов определенного типа (видео, аудио, pdf и т. Д.) На стороне сервера?

Ответы [ 9 ]

68 голосов
/ 22 ноября 2014

Один очень простой способ - использовать пользовательский валидатор.

В вашем приложении 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.')

Затем в вашем models.py:

from .validators import validate_file_extension

... и использовать валидатор для поля формы:

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

См. Также: Как ограничить типы файлов при загрузке файлов для ModelForms с FileFields? .

42 голосов
/ 21 апреля 2017

Django в версии 1.11 имеет недавно добавленный 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'])])

Обратите внимание, что этот метод небезопасен .Цитата из Django docs:

Не полагайтесь на проверку расширения файла для определения типа файла.Файлы могут быть переименованы, чтобы иметь любое расширение, независимо от того, какие данные они содержат.

Также имеется новый validate_image_file_extension (https://docs.djangoproject.com/en/dev/ref/validators/#validate-image-file-extension) для проверки расширений изображений (с помощью подушки).

10 голосов
/ 06 сентября 2010

Есть фрагмент Джанго , который делает это:

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()
7 голосов
/ 15 января 2012

Во-первых. Создайте файл с именем formatChecker.py внутри приложения, где у вас есть модель с FileField, которую вы хотите принять для определенного типа файлов.

Это ваш формат Checker.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

Затем вместо «FileField» используйте этот «ContentTypeRestrictedFileField».

Пример:

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.

3 голосов
/ 28 апреля 2017

Вы можете использовать приведенные ниже ограничения для типов файлов в вашей форме

file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))
2 голосов
/ 12 декабря 2017

Несколько человек предложили использовать 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.')

В этом примере проверяется только 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,))
2 голосов
/ 01 июня 2012

Я думаю, что вам лучше всего подойдет ExtFileField , который Доминик Роджер указал в своем ответе, и python-magic , о котором упоминал Даниэль Куинн, - лучший путь. Если кто-то достаточно умен, чтобы хотя бы изменить расширение, вы поймаете его с помощью заголовков.

1 голос
/ 10 февраля 2015

Вы можете определить список допустимых типов MIME в настройках, а затем определить валидатор, который использует python-magic для обнаружения MIME-типа и вызывает ValidationError, если MIME-тип не принят.Установите этот валидатор в поле формы файла.

Единственная проблема заключается в том, что иногда MIME-типом является application / octet-stream, который может соответствовать различным форматам файлов.Кто-то из вас преодолел эту проблему?

1 голос
/ 08 января 2015

Дополнительно я расширю этот класс дополнительным поведением.

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)

Когда мы создаем экземпляр с параметром accept = ". Pdf, .txt", во всплывающем окне со структурой файлов по умолчанию мы видим файлы с переданным расширением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...