Как проверить формат изображения в django ImageField

6 Dmitry [2013-12-24 15:37:00]

В нашем проекте используются Python 2.7, PIL 1.1.7 и Django 1.5.1. Существует ImageField, который работает нормально для многих форматов изображений, включая bmp, gif, ico, pnm, psd, tif и pcx. Однако требование заключается только в разрешении изображений png или jpg. Как это можно сделать?

Upd. Я знаю, что могу проверить расширение файла и HTTP Content-Type. Но ни один из методов не является надежным. Я спрашиваю, есть ли способ проверить загруженное содержимое файла для png/jpg.

python django python-imaging-library


3 ответа


5 Решение Stephen Paulger [2013-12-24 17:07:00]

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

Что вы можете сделать, это создать подкласс django.forms.fields.ImageField, чтобы расширить функциональность to_python.

Проверка типа файла, выполняемая в Django в to_python, выглядит так:

Image.open(file).verify()

Ваш подкласс может выглядеть примерно так.

class DmitryImageField(ImageField):

    def to_python(self, data):
        f = super(DmitryImageField, self).to_python(data)
        if f is None:
            return None

        try:
            from PIL import Image
        except ImportError:
            import Image

        # We need to get a file object for PIL. We might have a path or we might
        # have to read the data into memory.
        if hasattr(data, 'temporary_file_path'):
            file = data.temporary_file_path()
        else:
            if hasattr(data, 'read'):
                file = BytesIO(data.read())
            else:
                file = BytesIO(data['content'])

        try:
            im = Image.open(file)
            if im.format not in ('BMP', 'PNG', 'JPEG'):
                raise ValidationError("Unsupport image type. Please upload bmp, png or jpeg")
        except ImportError:
            # Under PyPy, it is possible to import PIL. However, the underlying
            # _imaging C module isn't available, so an ImportError will be
            # raised. Catch and re-raise.
            raise
        except Exception: # Python Imaging Library doesn't recognize it as an image
            raise ValidationError(self.error_messages['invalid_image'])

        if hasattr(f, 'seek') and callable(f.seek):
            f.seek(0)
        return f

Вы можете заметить, что это большая часть кода из ImageField.to_python и может предпочесть просто создать подкласс класса FileField вместо ImageField, а не подклассифицировать ImageField и дублировать большую часть его функций. В этом случае перед проверкой формата обязательно добавьте im.verify().

EDIT: я должен указать, что я не тестировал этот подкласс.


1 smeso [2013-12-24 17:11:00]

Вы можете использовать python-magic, обертку ctype вокруг libmagic, библиотеку, используемую file в Linux.

Из документа:

>>> import magic
>>> magic.from_file("testdata/test.pdf")
'PDF document, version 1.2'
>>> magic.from_buffer(open("testdata/test.pdf").read(1024))
'PDF document, version 1.2'
>>> magic.from_file("testdata/test.pdf", mime=True)
'application/pdf'

Однако этот метод просто взглянет на mime информацию. Вы все равно можете загрузить недействительный PNG с правильным mime или вставить несанкционированные данные в метаданные файла.


1 Chris Hawkes [2013-12-24 15:50:00]

Вы, вероятно, захотите использовать os для этого. Из документов Python.

os.path.splitext(путь) Разделите путь пути в пару (root, ext), так что root + ext == path и ext пуст или начинается с периода и содержит не более одного периода. Ведущие периоды в basename игнорируются; splitext ('.cshrc') возвращает ('.cshrc', ''). Изменено в версии 2.6: более ранние версии могут создавать пустой корень, когда единственным периодом был первый символ.

Пример

import os
fileName, fileExtension = os.path.splitext('yourImage.png')

print fileName 
>>> "yourImage"

print fileExtension
>>> ".png"

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