11

I tried to upload an .svg by the admin site to SQLite (django's default db) and I got the following error:

Upload a valid image. The file you uploaded was either not an image or a corrupted image.

I can upload .jpg files and it works properly.

class News(models.Model):
    news_id         = models.AutoField(primary_key=True, editable=False)
    news_title      = models.CharField(max_length=150)
    news_date       = models.DateTimeField(auto_now_add=True, editable=False)
    news_body       = models.TextField(max_length=1500)
    news_img        = models.ImageField(upload_to="pictures/%Y/%m/")
    news_author     = models.ManyToManyField(Author)

    class Meta:
        ordering: ['news_id']

    def __str__(self):
        return '%s %s %s'%(self.news_id, self.news_title, self.news_date)
VicenteC
  • 311
  • 7
  • 26
  • 1
    Does this answer your question? [Allow SVG files to be uploaded to ImageField via Django admin](https://stackoverflow.com/questions/38006200/allow-svg-files-to-be-uploaded-to-imagefield-via-django-admin) – mrts Oct 04 '20 at 06:22

2 Answers2

20

You sould change your Django ImageField to FileField and add .svg extension to field validators.

File Extension Validator

The result model file field is something like this:

from django.core.validators import FileExtensionValidator

news_img = models.FileField(upload_to="pictures/%Y/%m/", validators=[FileExtensionValidator(['pdf', 'doc', 'svg'])])
Jorge Alvarado
  • 372
  • 2
  • 12
  • Should there be any security concerns? SVG files are not only image files but also contain XML-based markup that can include scripts and potentially execute arbitrary code when rendered by a browser. Does Django check this stuff? – Solomon Botchway Jun 29 '23 at 09:46
-1

Create your own field like this:

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'

Reference: https://gist.github.com/ambivalentno/9bc42b9a417677d96a21