Django FileField, resize image before save to S3BotoStorage

Hi!

I have a FileField which acutally stores an image.
However, when the image is added or changed, I need to resize the image.
Storage backend is s3botostorage.
Here’s the mixin I wrote to resize the image which works well with local FileSystemStorage.

class ImageResizeMixin:
    def resize_image(self, imageField: Union[models.FileField, models.ImageField], size: tuple=(1200, 1200)):
        side = int(size[0])
        im = Image.open(imageField)
        # images of RGBA mode which has alpha channel
        # should be converted to RGB to remove alpha channel
        im = im.convert('RGB')
        width, height = im.size
        if width == height and width != side:
            resized_im = im.resize(size)
        else:
            # side which is smaller than desired size is streched to the size
            # and the other side will be cut off
            scale = side / min(width, height)
            if width < height:
                new_size = (side, int(height * scale))
                top = (new_size[1] - side) / 2
                crop_box = (0, top, side, new_size[1] - top)
            else:
                new_size = (int(width * scale), side)
                left = (new_size[0] - side) / 2
                crop_box = (left, 0, new_size[0] - left, side)
            
            resized_im = im.resize(new_size).crop(crop_box)
                
        output = BytesIO()
        resized_im.save(output, format='JPEG')
        output.seek(0)

        content_file = ContentFile(output.read())
        file = File(content_file)

        base = os.path.basename(imageField.name)
        filename = base.rsplit('.', 1)[0] + '.jpg'

        imageField.save(filename, file, save=False)

And this is my model’s save() method

def save(self, *args, **kwargs):
        ...
        
        this_object = MyModel.objects.get(pk=self.pk)

        super(MyModel, self).save(*args, **kwargs)
        if self.background and this_object.background != self.background:
            self.resize_image(self.background, (1200, 1200))

Here’s my problem.
I upload png file on django admin.
After saving the model, I see both png file and jpg file of same image on s3 bucket.
I don’t need png file as I convert all images to jpeg.
And it actually writes to s3 twice - one png and one jpg.
I want to resize before it is acutally written to storage so it only writes once to storage.

How can I solve this?
Any help would be appreicated.
@KenWhitesell , maybe this would be an easy fix for you. Any advice?

Well, I don’t have any specific knowledge in this area, but if I had to solve this, I’d be looking at a couple of different options.

In no particular order, and with no guarantees about any of them.

This will work well to resize the image while also preserving the image aspect ratio:

import PIL.Image

class Image(models.Model):
 
    image = models.ImageField(upload_to="images", blank=True)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        img = PIL.Image.open(self.image)
        width, height = img.size
        target_width = 600
        h_coefficient = width/600
        target_height = height/h_coefficient
        img = img.resize((int(target_width), int(target_height)), PIL.Image.ANTIALIAS)
        img.save(self.image.path, quality=100)
        img.close()
        self.image.close()

It’s not something I came up with myself, but I did have to modify if it a bit to work with Pillow 9.2.0.
I am using this in production with an NGINX proxy (serving images), and storage on Azure. It is working, and will reduce a 5MB image to 200KB (using image width of 600).