SebasSBM
SebasSBM

Reputation: 908

Why ImageField in form always triggers "invalid_image"?

I've implemented ImageField to upload images using Pillow verification in Django 1.8. For some reason, I can't submit the form. It always raises this ValidationError in the form (but with FileField this would work):

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

The weird part of all this is that the ImageField.check method seems to obtain correct MIME type! (see below)

WHAT I TRIED

I've tried with JPG, GIF, and PNG formats; none worked.

So I tried to print some variables in django.forms.fields.ImageField modifying the try statement that triggers this error, adding print statements for testing:

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(file)
    # verify() must be called immediately after the constructor.
    damnit = image.verify()
    print 'MY_LOG: verif=', damnit

    # Annotating so subclasses can reuse it for their own validation
    f.image = image
    # Pillow doesn't detect the MIME type of all formats. In those
    # cases, content_type will be None.
    f.content_type = Image.MIME.get(image.format)
    print 'MY_LOG: image_format=', image.format
    print 'MY_LOG: content_type=', f.content_type

Then I submit a form again to trigger the error after running python manage.py runserver and obtain these lines:

MY_LOG: verif= None

MY_LOG: image_format= JPEG

MY_LOG: content_type= image/jpeg

Image is correctly identified by Pillow and the try statement is executed until it's last line... and still the except statement is triggered? It makes nosense!

Using the same tactic, I tried to obtain sone useful log from django.db.models.fields.files.ImageField and every of it's parents until Field to print errors lists... all of them empty!

MY QUESTION

Is there anything else I can try to spot what is triggering the ValidationError?

SOME CODE

models.py

class MyImageModel(models.Model):
    # Using FileField instead would mean succesfull upload
    the_image = models.ImageField(upload_to="public_uploads/", blank=True, null=True)

views.py

from django.views.generic.edit import CreateView
from django.forms.models import modelform_factory

class MyFormView(CreateView):
    model = MyImageModel
    form_class = modelform_factory(MyImageModel,
                 widgets={}, fields = ['the_image',])

EDIT:

After trying the tactic suggested by @Alasdair, I obtained this report from e.message:

cannot identify image file <_io.BytesIO object at 0x7f9d52bbc770>

However, the file is successfully uploaded even if I'm not allowed to submit the form. It looks like if, somehow, the path to image wasn't processed correctly (or something else that hinders the image loading in these lines).

I think something is probably failing on these lines (from django.forms.fields.ImageField):

# 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'):
    file = data.temporary_file_path()
else:
    if hasattr(data, 'read'):
        file = BytesIO(data.read())
else:
    file = BytesIO(data['content'])

If I explore what properties does this class BytesIO have, maybe I can extract some relevant information about the error...

EDIT2

data attribute arrives empty! Determining why won't be easy...

Upvotes: 4

Views: 1736

Answers (2)

SebasSBM
SebasSBM

Reputation: 908

After thinking a lot, analyzing the implied code and lots of trial-and-error, I tried to edit this line from the try / except block that I exposed in the question (in django.forms.fields.ImageField) like this:

# Before edition
image = Image.open(file)

# After my edition
image = Image.open(f)

This fixed my issue. Now everything works well and I can submit the form. Invalid files are correctly rejected by the corresponding ValidationError

MY GUESS ABOUT HOW COULD THIS HAPPEN

I'm not sure if I'm guessing right, but:

I think this worked because this line had an error naming the correct variable. In addition, using file as a variable name looks like a typo, because file seems to be reserved for an existing built-in.

If my guess is right, maybe I should report this issue to Django developers

Upvotes: 0

GwynBleidD
GwynBleidD

Reputation: 20539

From django documentation:

Using an ImageField requires that Pillow is installed with support for the image formats you use. If you encounter a corrupt image error when you upload an image, it usually means that Pillow doesn’t understand its format. To fix this, install the appropriate library and reinstall Pillow.

So first, you should install Pillow, instead of PIL (pillow is an fork of PIL) and second, when installing, make sure that all libraries required for "understanding" by Pillow various image formats, are installed.

For list of dependencies, you can look into Pillow documentation.

Upvotes: 1

Related Questions