[Solved]-How to validate image format in django ImageField

10👍

You don’t specify whether you’re using a Django form to upload the image, I assume so as it is in the form field that the validation is carried out.

What you could do is create a subclass of django.forms.fields.ImageField to extend the functionality of to_python.

The file type check currently carried out in Django in to_python looks like this

Image.open(file).verify()

Your subclass could look something like.

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

You may notice this is most of the code from ImageField.to_python and might prefer to just create a sub-class of FileField to use instead of ImageField rather than subclassing ImageField and duplicating much of its functionality. In this case make sure to add im.verify() before the format check.

EDIT: I should point out that I’ve not tested this subclass.

1👍

You will probably want to use os for this. From the Python docs.

os.path.splitext(path)
Split the pathname path into a pair (root, ext) such that root + ext == path, and ext is empty or begins with a period and contains at most one period. Leading periods on the basename are ignored; splitext(‘.cshrc’) returns (‘.cshrc’, ”).
Changed in version 2.6: Earlier versions could produce an empty root when the only period was the first character.

example

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

print fileName 
>>> "yourImage"

print fileExtension
>>> ".png"

So once you have your ext separated from the filename you should just use a simple string comparison to verify it’s the right format.

1👍

You can use python-magic, a ctype wrapper around libmagic, the library used by the file on Linux.

From its doc:

>>> 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'

However this method simply look at mime information. You can still upload a non-valid PNG with the correct mime or embed unauthorized data in file’s metadata.

👤smeso

Leave a comment