how to compress the image before uploading to s3 in django?

Question:

I am working on an application where a user can upload an image. I want to reduce the size of the image in 200-500kb.
This is my models.py file

class Report_item(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    title = models.CharField(max_length=255, help_text='*Title for the post e.g. item identity')
    image = models.ImageField(default="add Item image",
                          upload_to=get_uplaod_file_name)


    def __str__(self):
        return self.title + "      " + str(self.publish)

And this is my views.py file

class ReportCreate(generic.CreateView):
model = Report_item
fields = ['title','image']

def get_form(self, form_class=None):
    if form_class is None:
        form_class = self.get_form_class()
    form = super(ReportCreate, self).get_form(form_class)
    form.fields['title'].widget = TextInput(
        attrs={'placeholder': '*Enter UID e.g. CBSE Marksheet Roll nunber 0506***'})
    return form

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.object.owner = self.request.user
    self.object.save()
    return FormMixin.form_valid(self, form)

I am using Django 1.11 and S3 storage. Kindly help me to compress the image before uploading to s3.

Asked By: imsaiful

||

Answers:

So we need to define a save method in models in order to compress the image before save. Following code help me what I want to achieve for my problem.

from io import BytesIO
import sys
from PIL import Image
from django.core.files.uploadedfile import InMemoryUploadedFile


class Report_item(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    title = models.CharField(max_length=255, help_text='*Title for the post e.g. item identity')
    
    image = models.ImageField(default="add Item image",
                              upload_to=get_uplaod_file_name)

    def save(self):
        # Opening the uploaded image
        im = Image.open(self.image)

        output = BytesIO()

        # Resize/modify the image
        im = im.resize((100, 100))

        # after modifications, save it to the output
        im.save(output, format='JPEG', quality=90)
        output.seek(0)

        # change the imagefield value to be the newley modifed image value
        self.image = InMemoryUploadedFile(output, 'ImageField', "%s.jpg" % self.image.name.split('.')[0], 'image/jpeg',
                                        sys.getsizeof(output), None)

        super(Report_item, self).save()

WIth the help of this 5 Mb image compress to 4 kb approx.

Answered By: imsaiful

Suppose you want to upload an image after resizing it. You can use below code, just pass the image object and you will get resized image in return.

def GetThumbnail(f):
    try:
        name = str(f).split('.')[0]
        image = Image.open(f)
        image.thumbnail((400, 400), Image.ANTIALIAS)
        thumbnail = BytesIO()
        # Default quality is quality=75
        image.save(thumbnail, format='JPEG', quality=50)
        thumbnail.seek(0)
        newImage = InMemoryUploadedFile(thumbnail,
                                   None,
                                   name + ".jpg",
                                   'image/jpeg',
                                   thumbnail.tell(),
                                   None)
        return newImage
    except Exception as e:
        return e
Answered By: Pratik Saluja

@jitesh2796, to retain the height to width ratio, modify the accepted answer to include the following:

def save(self):
       ...
       original_width, original_height = im.size
       aspect_ratio = round(original_width / original_height)
       desired_height = 100  # Edit to add your desired height in pixels
       desired_width = desired_height * aspect_ratio

        # Resize the image
        im = im.resize((desired_width, desired_height))
        ...

Note you may also need to use round() to ensure that the aspect ratio is an integer.

Answered By: TD1

This StackOverflow post shows you how to use django-resized to do this easily in your models.py.

Answered By: Anthony Petrillo