Search code examples
pythondjangopython-imaging-librarydjango-ninja

Downloading image created using Pillow with Django


I am creating image using Pillow from text input provided by user. I am using Django and django-ninja to process the data and then send the image to user. It will not show the image and my server will not store the image, the user will get to download the image.

The process will be like:

User type text > User press Download button > Ajax send text to Django backend > Django backend using Pillow create image based on the text > User download the image

There is no error and I can see the image from preview in network tab but I am not able to download the image.

This is my Ajax:

function downloadDoa(){
    allDoas = []
    for (let i = 0; i < userDoaAll.length; i++) {
        userCustomMadeDoa = userDoaAll[i];
        if (userCustomMadeDoa.classList.contains("customUserDoa")){
            allDoas.push(['na',userCustomMadeDoa.innerHTML])
        }
    }
    console.log(allDoas)
    $.ajax({
        url: "{% url 'api:download_doa' %}",
        data: {
            customDoasLists: JSON.stringify(allDoas),
            csrfmiddlewaretoken: '{{ csrf_token }}',
        },
        method : "post",
        dataType : "json",
        headers: {'X-CSRFToken': csrftoken},
        mode: 'same-origin', // Do not send CSRF token to another domain.
        csrfmiddlewaretoken: '{% csrf_token %}',
        success: function (response) {
            console.log(response)  
        },
    });
}

This is from my api.py to process user input:

@api.post("/download_doa",url_name="download_doa",auth=None)
def download_doa(request):
    im_io = io.BytesIO()
    text_list = json.loads(request.POST.get('customDoasLists'))
    font_path_a = "Hafs.ttf" 
    font_path_na = "Arial.ttf"
    font_size_a = 40
    font_size_na = 30
    text_color = (0, 0, 0)  # RGB color (black)
    background_color = (255, 255, 255, 255)  # RGBA color (white)
    output_image = text_to_image(text_list, font_path_a, font_size_a,font_path_na, font_size_na, text_color, background_color)
    output_image.save(im_io, 'png')
    im_io.seek(0)

    return HttpResponse(im_io,headers={'Content-Type': 'image/png',
                                       'Content-Disposition': 'attachment;filename="file_name.png"'})

This is from the network tab:

Network output

I think I missed few steps because the image is sent to frontend but it is not downloaded. Will be glad if someone can help, thank you.


Solution

  • Thanks to Tim Roberts specifying about the js didnt do anything, I did more googling and found the answer. Need to change ajax to this:

    $.ajax({
        url: "{% url 'api:download_doa' %}",
        data: {
            customDoasLists: JSON.stringify(allDoas),
            csrfmiddlewaretoken: '{{ csrf_token }}',
        },
        method : "post",
        headers: {'X-CSRFToken': csrftoken},
        mode: 'same-origin', // Do not send CSRF token to another domain.
        csrfmiddlewaretoken: '{% csrf_token %}',
        xhrFields:{
            responseType: 'blob'
        },
        success: function (response) {
            var link = document.createElement('a');
            link.href = URL.createObjectURL(response, {type: "image/png"})
            link.download = 'image.png';
            document.body.appendChild(link);
            link.click();
        },
    });
    

    Changes from my previous Ajax:

    1. Removed datatype "json" because the response from Django backend is not in the form of JSON
    2. Add xhr field with response type blob to process binary from Django backend.
    3. Add link and click the link to download the image

    Second and third changes are taken from here