Compress images in django
Tags: python, django, webdev, beginners
Published: 2019-07-17 04:28:16

---
title: Compress images in django
published: true
description: Data Compression is a vital part of all the web applications, and it reduces storage costs and helps the site scale at ease.
tags: Python, django, webdev, beginners
cover_image : https://miro.medium.com/max/1280/1*94MOUE4cCTVjK5n24tn-LA.jpeg
---

Storage is one of the essential pieces of web app development and needs to be paid special attention as it attracts server costs. If you have to pay for the storage costs of your web apps that involve images or any other non-text content it is better to focus on compression to reduce storage costs and scale at ease. This post discusses the image compression (lossy) in Django in detail and demonstrates an example of its implementation. You can find all the implementation details here.

## What is compression?

According to Wikipedia, data-compression involves encoding information using fewer bits than the original representation. To put it in simple words, compression is a method of making the file size smaller using a specific technique or algorithm.

Types and methods Data-compression is classified into two types.

**[Lossy](https://en.wikipedia.org/wiki/Lossy_compression)**: In this type of compression, the quality of data(in this case images and video) is reduced on compression. This is widely used to compress multimedia.

**[Lossless](https://en.wikipedia.org/wiki/Lossless_compression)**: In this type of compression, the quality of data(in this case image) is not lost. This is in wide use to compress sensitive data where data loss cannot be afforded.

## Installation and Configuration

Install the python image handling library `Pillow`

`pip install Pillow`

We will set up a simple project to demonstrate the image upload and the file sizes of before and after upload. You can also refer to [Django-docs](https://docs.djangoproject.com/en/2.0/) or download the completed [source code](https://github.com/gajeshbhat/django-experiments/tree/master/Upload-compress) here.

Our **models.py** looks like,
```
import sys
from django.db import models
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile

class Upload(models.Model):
    nameImage = models.CharField(max_length = 140,blank=False,null=True)
    uploadedImage =  models.ImageField(upload_to = 'Upload/',blank=False,null=True)
    def save(self, *args, **kwargs):
        if not self.id:
            self.uploadedImage = self.compressImage(self.uploadedImage)
        super(Upload, self).save(*args, **kwargs)
    def compressImage(self,uploadedImage):
        imageTemproary = Image.open(uploadedImage)
        outputIoStream = BytesIO()
        imageTemproaryResized = imageTemproary.resize( (1020,573) ) 
        imageTemproary.save(outputIoStream , format='JPEG', quality=60)
        outputIoStream.seek(0)
        uploadedImage = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" % uploadedImage.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)
        return uploadedImage
```
In the above `models.py` file we declare a utility method of the name `compressImage` and pass it the `uploadedImage` as a parameter. This method is called in `save` module while creating the `Upload` object.

We use a python image handling library [Pillow](https://www.geeksforgeeks.org/working-images-python/)(PIL) to handle image processing. We open the uploaded image and store it in a temporary object `imageTemproary` and we initialize a `BytesIO` stream to handle writing the [changes](https://docs.python.org/3/library/io.html)to the image. We use the `resize()` method to resize the uploaded image to a particular size (lesser than the original size in this case) and reset the output stream pointer stream to initial position using `seek()` method. We can further use django's `InMemoryUploadedFile` to overwrite the existing uncompressed image.

You can go ahead and customize the method to change the image type, size, and quality level according to your needs.

Our **views.py** looks like,

```
from django.shortcuts import render
from CompressUpload.models import Upload
from CompressUpload.forms import imageUploadForm

def displayUploadedFiles(request):
    uploadImageList = Upload.objects.all()
    return render(request, 'CompressUpload/list.html', {'uploadImageList': uploadImageList})

def uploadImage(request):
    imageUploadFormResult = imageUploadForm(request.POST, request.FILES)
    if request.method == 'POST':
        if imageUploadFormResult.is_valid():
            imageUploadFormResult.save()
        else:
            return render(request, 'CompressUpload/list.html', {'imageUploadFormResult': imageUploadFormResult})
    return render(request, 'CompressUpload/upload.html', {'imageUploadFormResult': imageUploadFormResult})
```

our **forms.py** looks like,

```
from django import forms
from CompressUpload.models import Upload

class imageUploadForm(forms.ModelForm):
    class Meta:
        model = Upload
        fields = ('Name','uploadedImage',)
```

Our **urls.py** (of the app in which models.py is present)

```
from django.conf.urls import url, include
from CompressUpload.views import displayUploadedFiles , uploadImage

urlpatterns = [
url(r'^list/$',displayUploadedFiles,name='List Uploaded Images'),
url(r'^upload/$',uploadImage,name='Upload Images')
]
```

An example of `upload.html` in the `templates` directory

```
<!DOCTYPE html>
<html>
<head>
<title>Upload Image</title>
</head>
<body>
<form id="create_podcast_form" method="post" enctype="multipart/form-data">
 {% csrf_token %}
 {{ imageUploadFormResult.as_p }}
 <input type="submit" value="Submit"/>
 </form>
</body>
</html>
```
The settings.py must contain the usual `static` and `media` settings to store the uploaded file. The setting given below is recommended.

```
TEMPLATES = [
  {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.normpath(os.path.join(BASE_DIR, 'templates')),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.request',
            ],
        },
    },
]

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/

```
Here’s an example image being uploaded, note that the size before the upload is 179.4 KB and after compressing and resizing is 83.9 KB. This can make a significant difference when it comes to files of larger sizes at scale.

![Image before compression.](https://thepracticaldev.s3.amazonaws.com/i/e3pnx9uuc4wbguz56tx9.png)
![Image after compression.](https://thepracticaldev.s3.amazonaws.com/i/ye47janr2y1opffmq3xt.png)

### Note on resizing and Quality tradeoff:

In this compression, we have performed a lossy compression to reduce the image size, and that comes with a quality trade-off. You can try different values (from 1–100) to suit your needs and also resize the image as per its use. To state an example, we can reduce the size of images that are only used in thumbnails to the size of the thumbnail allowed on the site. Further reading on this topic can be found in the References.

You can read more of my articles and follow me on [Medium](https://medium.com/@gajeshbhat) or [Twitter](https://twitter.com/gajeshbhat).

### References:
1. [Wikipedia page on Data Compression](https://en.wikipedia.org/wiki/Data_compression)
2. [Lossless compression further reading](https://en.wikipedia.org/wiki/Lossless_compression)
3. [Lossy compression] (https://en.wikipedia.org/wiki/Lossy_compression)
4. [Django Docs for creating a simple Django App](https://docs.djangoproject.com/en/2.0/)
5. [Image handling tutorial on GeeksForGeeks](https://www.geeksforgeeks.org/working-images-python/)
6. [BytesIO Documentation](https://docs.python.org/3/library/io.html#io.BytesIO)
7. [Django’s Uploaded file handler](https://docs.djangoproject.com/en/2.0/_modules/django/core/files/uploadedfile/#InMemoryUploadedFile)