Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A form field to handle validation of image + svg.
import sys
from io import BytesIO
from PIL import Image
import xml.etree.cElementTree as et
from django.core.exceptions import ValidationError
from django.forms import ImageField as DjangoImageField
from django.utils import six
class SVGAndImageFormField(DjangoImageField):
def to_python(self, data):
"""
Checks that the file-upload field data contains a valid image (GIF, JPG,
PNG, possibly others -- whatever the Python Imaging Library supports).
"""
test_file = super(DjangoImageField, self).to_python(data)
if test_file is None:
return None
# We need to get a file object for Pillow. We might have a path or we might
# have to read the data into memory.
if hasattr(data, 'temporary_file_path'):
ifile = data.temporary_file_path()
else:
if hasattr(data, 'read'):
ifile = BytesIO(data.read())
else:
ifile = BytesIO(data['content'])
try:
# load() could spot a truncated JPEG, but it loads the entire
# image in memory, which is a DoS vector. See #3848 and #18520.
image = Image.open(ifile)
# verify() must be called immediately after the constructor.
image.verify()
# Annotating so subclasses can reuse it for their own validation
test_file.image = image
test_file.content_type = Image.MIME[image.format]
except Exception:
# add a workaround to handle svg images
if not self.is_svg(ifile):
six.reraise(ValidationError, ValidationError(
self.error_messages['invalid_image'],
code='invalid_image',
), sys.exc_info()[2])
if hasattr(test_file, 'seek') and callable(test_file.seek):
test_file.seek(0)
return test_file
def is_svg(self, f):
"""
Check if provided file is svg
"""
f.seek(0)
tag = None
try:
for event, el in et.iterparse(f, ('start',)):
tag = el.tag
break
except et.ParseError:
pass
return tag == '{http://www.w3.org/2000/svg}svg'
@jakubste

This comment has been minimized.

Copy link

@jakubste jakubste commented Jan 12, 2017

In line 44 ifile can be a filepath. Then when it is passed to is_svg it blows up, cause it tries to launch seek method on string object. Need to open this file before seek.

@ruchej

This comment has been minimized.

Copy link

@ruchej ruchej commented Jan 12, 2020

Dont work on django 3
from django.utils import six

@gtljaymodi

This comment has been minimized.

Copy link

@gtljaymodi gtljaymodi commented Jan 18, 2020

@ramsrib

This comment has been minimized.

Copy link

@ramsrib ramsrib commented Jun 22, 2020

In latest version of django, you need to update the exception like this (replace line 45-48):

raise ValidationError(
    self.error_messages['invalid_image'],
    code='invalid_image',
).with_traceback(sys.exc_info()[2])
@ramsrib

This comment has been minimized.

Copy link

@ramsrib ramsrib commented Jun 22, 2020

Also you need to add default validator to this form field to make it work in Django 3. Refer this: https://gist.github.com/ramsrib/4ca2d66fa0063e5acf8b874ecb48cc5f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment