Django-storages: Value Error when uploading resized images to Google Cloud Storage

Created on 4 Feb 2019  路  5Comments  路  Source: jschneier/django-storages

My image model contains;

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
    image_asset = img.open(self.image)
    image_name = uuid.uuid4()

    image_asset.thumbnail((1200, 1200), img.ANTIALIAS)
    fi_io = io.BytesIO()
    image_asset.save(fi_io, format='JPEG', quality=90)
    self.image = InMemoryUploadedFile(
        fi_io,
        'ImageField',
        '%s.jpg' % image_name,
        'image/jpeg',
        sys.getsizeof(fi_io), None
    )

    super(Image, self).save(force_update=force_update)

Whenever I attempt to save an image I get this error (the exact size depends on the file uploaded):

ValueError: Size 3984 was specified but the file-like object only had 3862 bytes remaining.

Without this code, the image saves fine. Also when switching to a Digital Ocean storage this exact code works without a problem.

google

Most helpful comment

I think this is an error in the end user code which GCS rejects and the other services are more liberal about. The call sys.getsizeof(fi_io) yields the size of the BytesIO object, not the size of the buffer:

>>> iobuffer = io.BytesIO()  # empty buffer (0 bytes)
>>> sys.getsizeof(iobuffer)
88
>>> len(iobuffer.getbuffer())
0
>>> 

This following code works for me with django-storages and GCS when the returned thumbnail is saved to the model:

def generate_thumbnail(src):
    image = Image.open(src)  # in memory
    image.thumbnail(settings.THUMBNAIL_SIZE, Image.ANTIALIAS)
    buffer = BytesIO()
    image.save(buffer, 'JPEG')
    file_name = Path(src.name).name

    temp_file = InMemoryUploadedFile(buffer, None, file_name, 'image/jpeg', len(buffer.getbuffer()), None)
    return temp_file

All 5 comments

@designkai Did you ever figure this out?

@sww314 Nope, I had to switch to AWS for file storage.

I think this is an error in the end user code which GCS rejects and the other services are more liberal about. The call sys.getsizeof(fi_io) yields the size of the BytesIO object, not the size of the buffer:

>>> iobuffer = io.BytesIO()  # empty buffer (0 bytes)
>>> sys.getsizeof(iobuffer)
88
>>> len(iobuffer.getbuffer())
0
>>> 

This following code works for me with django-storages and GCS when the returned thumbnail is saved to the model:

def generate_thumbnail(src):
    image = Image.open(src)  # in memory
    image.thumbnail(settings.THUMBNAIL_SIZE, Image.ANTIALIAS)
    buffer = BytesIO()
    image.save(buffer, 'JPEG')
    file_name = Path(src.name).name

    temp_file = InMemoryUploadedFile(buffer, None, file_name, 'image/jpeg', len(buffer.getbuffer()), None)
    return temp_file

Hi bgrace i followed the above, but it doesnt work, the default unsized image is being stored.

models.py

class Product(models.Model):
name=models.CharField(max_length=100)
image=models.ImageField(default='default.jpg',upload_to='productimages')
price=models.FloatField()

def generate_thumbnail(self,src):
    image = Image.open(src)  # in memory
    image.thumbnail((400,300), Image.ANTIALIAS)
    buffer = BytesIO()
    image.save(buffer, 'JPEG')
    file_name = os.path.join(settings.MEDIA_ROOT, self.image.name)
    temp_file = InMemoryUploadedFile(buffer, None, file_name, 'image/jpeg', len(buffer.getbuffer()), None)
    return temp_file


def save(self, *args, **kwargs):
    self.image=self.generate_thumbnail(self.image)
    print(self.image.width) #prints the original size
    print(self.image.height)
    super(Product,self).save(*args, **kwargs)

@samuel88-cloud Just a guess but perhaps the call to save with the same arguments is overwriting your change to self.image. Consider using a post-save signal to generate the thumbnails. In any case it's not an issue with django-storages so questions should be taken to the support channel for PIL (or whatever image library you're using). Also look into using the Decimal type, not Float, for your price field, Float is not suitable for currency. See https://docs.python.org/3.8/library/decimal.html and https://husobee.github.io/money/float/2016/09/23/never-use-floats-for-currency.html

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sheepeatingtaz picture sheepeatingtaz  路  7Comments

TreHack picture TreHack  路  8Comments

icpetcu picture icpetcu  路  8Comments

jonashaag picture jonashaag  路  4Comments

pratyushsharma picture pratyushsharma  路  6Comments