This site runs best with JavaScript enabled.

Django Custom Upload Handler to Compress Image Files

How to compress images when they are saved in a Django application.

Django Custom Upload Handler to Compress Image Files Django Custom Upload Handler to Compress Image Files

Photo by Patrick Tomasso on Unsplash

I got a somewhat unique request on a project the other day. My client has a lead tracking system where his salesman input leads and often upload scanned documents to include with the leads. I implemented this all with standard Django forms and a formset wizard to input multiple files.

My client was worried that a lot of images would be uploaded and he would have to start paying extra for storage. He asked if I could compress images on upload to save space. After searching the web I found examples of a few different ways of doing it. But after reading about Upload Handlers in the Django docs, this seemed like it would be the best method for accomplishing this so I wouldn't have to modify my models or forms at all. Unfortunately for me, it didn't go as straightforward as I had hoped. I couldn't find a good example of someone else doing this sort of thing and it took me MUCH longer than the 30-45 minutes I had planned for.

The good news is that I figured it out so I'm posting it here for all to benefit hopefully.

I created a file named uploadhandlers.py in my app and added the following code:

    import os

    from django.conf import settings
    from django.core.files.uploadhandler import MemoryFileUploadHandler
    from PIL import Image

    try:
        from cStringIO import StringIO
    except ImportError:
        from StringIO import StringIO

    class CompressImageUploadHandler(MemoryFileUploadHandler):
        def file_complete(self, file_size):
            """
            Return a file object if we're activated.
            """
            self.file.seek(0)
            if not self.content_type is None and 'image' in self.content_type:
                newfile = StringIO()
                img = Image.open(self.file)
                width, height = img.size
                width, height = scale_dimensions(width, height, longest_side=settings.IMAGE_LONGEST_SIDE)
                img = img.resize((width, height), Image.ANTIALIAS)
                img.save(newfile, 'JPEG', quality=settings.JPEG_QUALITY)
                self.file = newfile

                name, ext = os.path.splitext(self.file_name)
                self.file_name = '{0}.{1}'.format(name, 'jpg')
                self.content_type = 'image/jpeg'

            return super(CompressImageUploadHandler, self).file_complete(file_size)

    def scale_dimensions(width, height, longest_side):
        if width  1:
            return longest_side, int(longest_side / ratio)
        # Portrait
        else:
            return int(longest_side * ratio), longest_side

You can see from the code that I am simply extending the MemoryFileUploadHandler, which is one of the Django default upload handlers. I'm overriding the file_complete function to change the size and jpeg quality - which are settings in my settings file.

To implement the change, I update my views. The view that contains the form has to be csrf_exempt, and the view handling the uploads switches to this upload handler on the fly with the following code:

    request.upload_handlers.insert(0, CompressImageUploadHandler())

Discuss on 𝕏 Edit post on GitHub