This site runs best with JavaScript enabled.

Django Custom Upload Handler to Compress Image Files

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

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())
Share article
Dustin Davis

Dustin Davis is a software engineer, people manager, hacker, and entreprenuer. He loves to develop systems and automation. He lives with his wife and five kids in Utah.