DEV Community

Uhtred M.
Uhtred M.

Posted on • Edited on

9 3

Creating Image from dataURL base64 with Pyhton Django

2023 Update! I just rewrite this article in my website. See how to do with only Python 3.11 and also with Django 4.2.

Access the article here!


A few days ago, while developing the bazaar article register, I fell into a somewhat complicated situation. Perhaps yours is similar.

When creating a new article in the bazaar, the selected images are read using the FileReader API to generate the dataURL of the images in order to save it allowing the user to leave the bazaar, go back and continue creating the article with the selected images.

As the reference of the file of each image is lost, when uploading the images it was necessary to send the dataURL instead of the file itself, in this case the API should be in charge of generating the image files


Note: Using Python 3.6.9 and Django in version 3.0.3

For this, the following libraries were necessary to carry out this small task.

# python standard lib
import base64, secrets, io

# django and pillow lib
from PIL import Image
from django.core.files.base import ContentFile
Enter fullscreen mode Exit fullscreen mode

I defined this function capable of receiving the dataURL to generate the file and resize it if necessary.

def get_image_from_data_url( data_url, resize=True, base_width=600 ):

    # getting the file format and the necessary dataURl for the file
    _format, _dataurl       = data_url.split(';base64,')
    # file name and extension
    _filename, _extension   = secrets.token_hex(20), _format.split('/')[-1]

    # generating the contents of the file
    file = ContentFile( base64.b64decode(_dataurl), name=f"{_filename}.{_extension}")

    # resizing the image, reducing quality and size
    if resize:

        # opening the file with the pillow
        image = Image.open(file)
        # using BytesIO to rewrite the new content without using the filesystem
        image_io = io.BytesIO()

        # resize
        w_percent    = (base_width/float(image.size[0]))
        h_size       = int((float(image.size[1])*float(w_percent)))
        image        = image.resize((base_width,h_size), Image.ANTIALIAS)

        # save resized image
        image.save(image_io, format=_extension)

        # generating the content of the new image
        file = ContentFile( image_io.getvalue(), name=f"{_filename}.{_extension}" )

    # file and filename
    return file, ( _filename, _extension )
Enter fullscreen mode Exit fullscreen mode

Well, at this point, we don't actually have the generated file yet, just the contents of the file (Using the Django ContentFile API that returns an instance of it) stored in memory and ready to be written to a file in the filesystem

However, with the file that is returned, we only need to do the following to generate the file and save usingdefualt filesystem storage:

Example:

# for example
from .models import User
from .utils.images import get_image_from_data_url

def create_user_view(request)

    username = request.POST.get('username')
    # getting the file instance
    avatar_file = get_image_from_data_url(request.POST.get('avatar'))[0]

    # create a user and generate and save the file using the default filesystem storage
    user = User.objects.create(
        username=username,
        avatar=avatar_file
    )

    # this does not work 😂
    return Response(user, status=status.HTTP_201_CREATED)
Enter fullscreen mode Exit fullscreen mode

Of course you will do more validations, after all in your case it will not be a mere example right 😉


As a bonus 😎, I defined a second function capable of generating the content of the file for the main image and for a second thumbnail image (image copy but with a much smaller size, suitable for lazy load images among other situations).

def get_image_and_thumbnail_from_data_url( data_url, resize=True, base_width=600):
    #
    file, filename = get_image_from_data_url(data_url, resize, base_width)

    #
    thumbnail = Image.open(file)

    #
    thumbnail_io = io.BytesIO()
    thumbnail.thumbnail((128,128), Image.ANTIALIAS)
    thumbnail.save(thumbnail_io, format=filename[1])

    # thumbnail image
    thumbnail = ContentFile( 
        thumbnail_io.getvalue(), 
        name=f"{filename[0]}.thumbnail.{filename[1]}"
    )

    return file, thumbnail
Enter fullscreen mode Exit fullscreen mode

So, that's it for now! I hope this little tip can help you. But be careful that depending on the situation, these operations can be very costly. In case of multiple images, it is advisable to perform operations in the background, using Celery for example (maybe I will do a second article implementing Celery, what do you think? 🤔).

So that the user does not wait long before receiving a response from the server.


Be sure to comment on your debt, criticism or suggestion! Enjoy and follow my work on social networks.

Uhtred M.
Instagram


Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (2)

Collapse
 
horiyomi profile image
horiyomi • Edited

This came in really handy...thanks

Collapse
 
uhttred profile image
Uhtred M.

Amazing to know that I could help you 🥳

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay