Upload base64 images to the Django Rest framework

Last updated: Sept. 18, 2024

Base64 is a binary-to-text encoding scheme that allows binary data, such as images, to be represented as ASCII characters. Base64 encoding is commonly used when you need to transmit binary data in JSON or text format. Base64 strings can be placed inside JSON data, making it simpler and easier to upload images.

 

Steps for uploading base64 images:

  1. Read the image file: Read the binary data of the image file you want to upload.
  2. Encode the image as Base64: Convert the binary image data into a Base64-encoded format using an appropriate encoding algorithm or library.
  3. Convert to string: Convert the Base64-encoded data into a string format, as it needs to be treated as text for transmission or storage.
  4. Transmit the Base64 string: Send the Base64 string over a network in JSON format to the API

In this article, we will create an API using the Django Rest framework that accepts JSON data and converts base64 strings to images. The converted image will be saved on the server.

Prerequisites:

  1. Basic Knowledge of Django and the Django Rest Framework
  2. Basic Knowledge of JavaScript

Base64 image uploading in Django steps:

  1. Install drf-extra-fields
  2. Create a model with an image fields
  3. Create a serializer with Base64ImageField from drf-extra-fields
  4. Create a function base view
  5. Add the view in urls.py and create a new route

Django Implementations of Base64Image upload:

1/5. Install drf-extra-fields:

DRF Extra Fields' Base64ImageField is a powerful addition to Django REST Framework that enables seamless handling of image uploads. It allows you to accept image data as a Base64-encoded string directly within your API payload, eliminating the need for separate file uploads. This field automatically decodes the Base64 string and saves the image file to your specified storage backend, while still providing all the benefits of DRF's serializers and validators. With Base64ImageField, you can simplify the process of accepting and processing image uploads in your Django REST API with ease and efficiency.

pip install drf-extra-fields

 

2/5. Create a model with an image fields:

I created a model named Article. image field is necessary to save an image on the server disk and store the path in the database.

file_location is a function to dynamically create a path for saving images.

from django.db import models


def file_location(instance, filename, **kwargs):
    file_path = f"article/{instance.title}-{filename}"
    return file_path


class Article(models.Model):
    title = models.CharField(max_length=255, null=False, blank=False)
    content = models.TextField(max_length=5000, null=False, blank=False)
    image = models.ImageField(upload_to=file_location, null=False, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="created_at")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="updated_at")

    def __str__(self):
        return self.title

 

3/5. Create a serializer with Base64ImageField from drf-extra-fields:

We have created a serializer called ArticleBase64Serilizer. It has three fields named title, content, and image. An image field is a property of a model that represents an ImageField. But in ArticleBase64Serilizer, we override it using Bae64ImageField. This field will accept image data as a Base64-encoded string in the API payload and automatically convert and image

from rest_framework import serializers
from myapp.models import Article
from drf_extra_fields.fields import Base64ImageField


class ArticleBase64Serilizer(serializers.ModelSerializer):
    image = Base64ImageField(required=False)

    class Meta:
        model = Article
        fields = ["title", "content", "image"]

 

4/5. Create a function base view:

Create a function-based view named base64_image_upload_api_view. That receives JSON data form client and validates sending data by ArticleBase64Serilizer, and if the data is valid, then saves it in the database and also saves an image on disk.

from rest_framework.response import Response
from rest_framework.decorators import api_view
from myapp.api.serializers import ArticleBase64Serilizer


@api_view(["POST"])
def base64_image_upload_api_view(request):
    if request.method == "POST":
        data = request.data
        serializer = ArticleBase64Serilizer(data=data)

        if serializer.is_valid():
            article = serializer.save()
            data = serializer.data
            return Response(data=data)
        return Response(serializer.errors, status=400)

 

5/5. Add the view in urls.py and create a new route:

from myapp.api.views import base64_image_upload_api_view


urlpatterns = [
    path(
"api/base64_image_upload",
 base64_image_upload_api_view, 
name="base64_image_upload",
),
]
Base64 image upload using django rest framework

 

Demo base64 image with JSON for testing API:

{
    "title" : "Testing title",
    "content" : "demo content",
    "image" : "<base64 string>"
}

OR

{
    "title" : "Testing title",
    "content" : "demo content",
    "image" : "data:image/png;base64, <base64 string>"
}

You can use online image to base64 converter tools to generate base64 string for tesing API.

online image to base64 converter tools to generate base64 string for tesing API

 

JavaScript Steps:

  1. Create a templates/html file with image uploading form
  2. Create a function for image to base64 convert
  3. Create an axios request for uploading base64 image
  4. Create a view in views.py
  5. Add the view in urls.py and create a new route

 

JavaScript base64 image uploading implementations:

 

1/5. Create a templates/html file with image uploading form:

<!DOCTYPE html>
<html lang="en">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Base64 Image Upload</title>
    <style>
        body {
            padding: 1rem;
        }

        form {
            width: 50vw;
            height: fit-content;
        }
    </style>
</head>

<body>
    <form method="POST" onsubmit="image_to_base64(event)">
        <label for="title">Title</label>
        <br>
        <input type="text" name="title" id="title" placeholder="title">
        <br>
        <br>
        <label for="content">Content</label>
        <br>
        <textarea name="content" id="content" placeholder="Content"></textarea>
        <br>
        <br>
        <label for="image">Image</label>
        <input type="file" name="image" id="image" accept="image/*">
        <br>
        <button type="submit">Submit</button>
    </form>


</body>

</html>

2/5. Create a function for image to base64 convert

function image_to_base64(event) {
            event.preventDefault();
            var file = document.getElementById("image").files[0];
            var reader = new FileReader();
            reader.onloadend = function () {
                console.log(reader.result)
                upload_data(reader.result)
            }
            var text = reader.readAsDataURL(file);
}

3/5. Create an axios request for uploading base64 image:

function upload_data(base64_string) {
            event.preventDefault();
            var title = document.getElementById("title").value
            var content = document.getElementById("content").value
            var json_data = {
                "title": title,
                "content": content,
                "image": base64_string
            }
            axios.post(
                    '/api/base64_image_upload',
                    json_data, {
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        xsrfCookieName: 'csrftoken',
                        xsrfHeaderName: 'X-CSRFTOKEN'
                    })
                .then(function (response) {
                    console.log(response);
                })
                .catch(function (error) {
                    console.log(error);
                });
}

4/5. Create a view in views.py :

def upload_base64_image_view(request):
    return render(request, "base64_image_upload.html", {})

5/5. Add the view in urls.py and create a new route:

from myapp.api.views import base64_image_upload_api_view

from myapp.views import upload_base64_image_view

urlpatterns = [

    path(
        "api/base64_image_upload",
        base64_image_upload_api_view,
        name="base64_image_upload",
    ),
    path("base64_image", upload_base64_image_view, name="base64_image"),
]

Full Code:

<!DOCTYPE html>
<html lang="en">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Base64 Image Upload</title>
    <style>
        body {
            padding: 1rem;
        }

        form {
            width: 50vw;
            height: fit-content;
        }
    </style>
</head>

<body>
    <form method="POST" onsubmit="image_to_base64(event)">
        <label for="title">Title</label>
        <br>
        <input type="text" name="title" id="title" placeholder="title">
        <br>
        <br>
        <label for="content">Content</label>
        <br>
        <textarea name="content" id="content" placeholder="Content"></textarea>
        <br>
        <br>
        <label for="image">Image</label>
        <input type="file" name="image" id="image" accept="image/*">
        <br>
        <button type="submit">Submit</button>
    </form>

    <!-- Axios -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.4.0/axios.min.js"
        integrity="sha512-uMtXmF28A2Ab/JJO2t/vYhlaa/3ahUOgj1Zf27M5rOo8/+fcTUVH0/E0ll68njmjrLqOBjXM3V9NiPFL5ywWPQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    <script>
        function upload_data(image) {
            event.preventDefault();
            var title = document.getElementById("title").value
            var content = document.getElementById("content").value
            var json_data = {
                "title": title,
                "content": content,
                "image": image
            }
            axios.post(
                    '/api/base64_image_upload',
                    json_data, {
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        xsrfCookieName: 'csrftoken',
                        xsrfHeaderName: 'X-CSRFTOKEN'
                    })
                .then(function (response) {
                    console.log(response);
                })
                .catch(function (error) {
                    console.log(error);
                });
        }

        function image_to_base64(event) {
            event.preventDefault();
            var file = document.getElementById("image").files[0];
            var reader = new FileReader();
            reader.onloadend = function () {
                console.log(reader.result)
                upload_data(reader.result)
            }
            var text = reader.readAsDataURL(file);
        }
    </script>
</body>

</html>

2 comments

By Alex
Sept. 30, 2023, 12:06 a.m.
how to hide /api/base64_image_upload and /api/upload_article on server? im in doubt, - do we still need such construct, instead sending base64 data as string thru django forms and assembly it back on server?
By Educodiv Admin
Oct. 25, 2023, 3:35 a.m.
If you are working with django rest framework on that we must use this kind of construction. When your image upload api is simple on that case multiple-part are very use full but when you send complex json with image base64 upload is very necessary

Create new comment