Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate image format in django ImageField

Our project uses Python 2.7, PIL 1.1.7 and Django 1.5.1. There is an ImageField which works OK for many image formats, including bmp, gif, ico, pnm, psd, tif and pcx. However the requirement is to only allow png or jpg images. How can it be done?

Upd. I know I can validate file extension and http Content-Type header. But neither method is reliable. What I'm asking is whether there's a way to check uploaded file content for being png/jpg.

like image 682
Dmitry Avatar asked Dec 24 '13 12:12

Dmitry


3 Answers

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.

like image 102
Stephen Paulger Avatar answered Sep 22 '22 07:09

Stephen Paulger


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.

like image 40
Chris Hawkes Avatar answered Sep 21 '22 07:09

Chris Hawkes


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.

like image 43
smeso Avatar answered Sep 25 '22 07:09

smeso