Search code examples
pythondjangoamazon-s3python-imaging-library

Django Resize Image Before Saving


Goal: Upload a resized image (with same file name and aspect ratio) to AWS S3.

Problem: Currently upon saving, the original image is uploaded and not the resized one.

What have I tried?: I've tried multiple different ways to accomplish this but I run into various issues such as not the correct aspect ratio, poor image quality (when using django-resize) etc. The code below seems really close but I just can't seem to find where I am going wrong.

models.py

class Profile(BaseModel):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
    image = models.ImageField(default='default.jpg', upload_to='profile_pics')


    def save(self, commit=True, *args, **kwargs): #Edited

        if commit:
            img = Image.open(self.image)

            if img.height > 300 or img.width > 300:
                output_size = (300, 300)
                img.thumbnail(output_size)
                img.save(self.image.name, optimize=True, quality=100)

            super().save()


Solution

  • Solution:

    After a very long time I finally found the answer in this blog.

    In the end I made a new function in the users/utils.py file:

    from django.core.files import File
    from pathlib import Path
    from PIL import Image
    from io import BytesIO
    
    image_types = {
        "jpg": "JPEG",
        "jpeg": "JPEG",
        "png": "PNG",
        "gif": "GIF",
        "tif": "TIFF",
        "tiff": "TIFF",
    }
    
    def image_resize(image, width, height):
        # Open the image using Pillow
        img = Image.open(image)
        # check if either the width or height is greater than the max
        if img.width > width or img.height > height:
            output_size = (width, height)
            # Create a new resized “thumbnail” version of the image with Pillow
            img.thumbnail(output_size)
            # Find the file name of the image
            img_filename = Path(image.file.name).name
            # Spilt the filename on “.” to get the file extension only
            img_suffix = Path(image.file.name).name.split(".")[-1]
            # Use the file extension to determine the file type from the image_types dictionary
            img_format = image_types[img_suffix]
            # Save the resized image into the buffer, noting the correct file type
            buffer = BytesIO()
            img.save(buffer, format=img_format)
            # Wrap the buffer in File object
            file_object = File(buffer)
            # Save the new resized file as usual, which will save to S3 using django-storages
            image.save(img_filename, file_object)
    
    

    and then overwrote the save() function in the models.py:

    models.py

    from users.utils import image_resize
    
    class Profile(BaseModel):
        #some other fields
        image = models.ImageField(default='default.jpg', upload_to='profile_pics')
        
    
        def save(self, commit=True, *args, **kwargs):
    
            if commit:
                image_resize(self.image, 250, 250)
                super().save(*args, **kwargs)