Creating Image from dataURL base64 with Pyhton Django
Tags: python, django, webdev, tutorial
Published: 2020-03-30 13:28:05

**2023 Update**! I just rewrite this article in my [website](https://uhtred.dev/insights/creating-image-from-dataurl-base64-with-python-and-django). See how to do with only `Python 3.11` and also with `Django 4.2`.

Access the article [here](https://uhtred.dev/insights/creating-image-from-dataurl-base64-with-python-and-django)!

-------------

A few days ago, while developing the [bazaar](https://bazaar.ao) article register, I fell into a somewhat complicated situation. Perhaps yours is similar.

When creating a new article in the [bazaar](https://bazaar.ao), 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
# python standard lib
import base64, secrets, io

# django and pillow lib
from PIL import Image
from django.core.files.base import ContentFile
```

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

```python
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 )
```

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 using` defualt filesystem storage`:

**Example:**

```python
# 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)
```

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).

```python
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
```

------------------------------------------------------------------------------

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](https://www.celeryproject.org/) for example (maybe I will do a second article implementing [Celery](https://www.celeryproject.org/), 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.](https://uhtred.dev)
[Instagram](https://instagram.com/uhtred.dev)

------------------------------------------------------------------------------