Stefan Manastirliu Log



Django - Resize image on upload, before saving it to disk

I’ve got a Model with an ImageField, and i needed to resize the uploaded image (done through a modelform) before saving it to the disk: resizing the image on memory (or when it’s saved on a temp folder).

It took me a lot of time to understand how django’s InMemoryUploadedFile is done, and how to override the ImageField related file with the new resized one, so hope this can be usefull to someone.

Here is my code

def handle_uploaded_image(i):
        import StringIO
        from PIL import Image, ImageOps
        import os
        from django.core.files import File
        # read image from InMemoryUploadedFile
        str = “”
        for c in i.chunks():
            str += c
       
        # create PIL Image instance
        imagefile  = StringIO.StringIO(str)
        image = Image.open(imagefile)
   
        # if not RGB, convert
        if image.mode not in (“L”, “RGB”):
            image = image.convert(“RGB”)
       
        #define file output dimensions (ex 60x60)
        x = 130
        y = 130
       
        #get orginal image ratio
        img_ratio = float(image.size[0]) / image.size[1]
       
        # resize but constrain proportions?
        if x==0.0:
            x = y * img_ratio
        elif y==0.0:
            y = x / img_ratio
       
        # output file ratio
        resize_ratio = float(x) / y
        x = int(x); y = int(y)
       
        # get output with and height to do the first crop
        if(img_ratio > resize_ratio):
            output_width = x * image.size[1] / y
            output_height = image.size[1]
            originX = image.size[0] / 2 - output_width / 2
            originY = 0
        else:
            output_width = image.size[0]
            output_height = y * image.size[0] / x
            originX = 0
            originY = image.size[1] / 2 - output_height / 2
       
        #crop
        cropBox = (originX, originY, originX + output_width, originY + output_height)
        image = image.crop(cropBox)
       
        # resize (doing a thumb)
        image.thumbnail([x, y], Image.ANTIALIAS)

        # re-initialize imageFile and set a hash (unique filename)
        imagefile = StringIO.StringIO()
        filename = hashlib.md5(imagefile.getvalue()).hexdigest()+’.jpg’
   
        #save to disk
        imagefile = open(os.path.join(‘/tmp’,filename), ‘w’)
        image.save(imagefile,’JPEG’, quality=90)
        imagefile = open(os.path.join(‘/tmp’,filename), ‘r’)
        content = File(imagefile)
       
        return (filename, content)

#views.py

form = YourModelForm(request.POST, request.FILES, instance=profile)
        if form.is_valid():
            ob = form.save(commit=False)
            try:
                t = handle_uploaded_image(request.FILES[‘icon’])
                ob.image.save(t[0],t[1])
            except KeyError:
                ob.save()

Th basic aproach is reading the uploaded image from memory, create another one image on momory, using StringIO and PIL, then resize the image (note than it also makes a crop), and finaly save it to the disk.

Then we return back the filename and content(a File instance), witch are used to override the default ImageField.

I know this code may be improved a lot, hope someone can do it, anyway it makes the job done.

I know these things can be very annoying, so if you have questions, just ask.