1

I am trying to download a file when a user clicks on a particular button. This file is an image which gets created when the said button is pressed. What I want is, it should automatically download the image on the client's device.

I am using Flask on the server code, and ideally, the send_file function of Flask should trigger this auto download as it adds the Content-Disposition header.

On the client side, I have a JS code which uses fetch API to send a POST request to the server with some data, which is used for generating the image which is to be downloaded.

This is the JS code:

function make_image(text){
    const json={
        text: text
    };
    const options={
        method: "POST",
        body: JSON.stringify(json),
        headers:{
            'Content-Type':'application/json',
        }
    };

    fetch('/image',options)
        .then(res=>{
            res.json(); //Gives an error: Uncaught (in promise) SyntaxError: Unexpected token � in JSON at position 0
        }).catch(err=>console.log(err));
}

And this is the Python code on the server:

@app.route('/image',methods=['POST'])
def generate_image():
    cont = request.get_json()
    t=cont['text']
    print(cont['text'])
    name = pic.create_image(t)
    time.sleep(2)
    return send_file(f"{name}.png",as_attachment=True,mimetype="image/png")

But nothing is happening. The image doesnt get downloaded. However,the image is getting created on the server and is not corrupt

How do I resolve this? And is there some other way to do what I am trying to do?

RishiC
  • 768
  • 1
  • 10
  • 34
  • If you're trying to get an ajax request to handle something as a downloadable attachment, you'll need to use something along these lines: https://stackoverflow.com/questions/32545632/how-can-i-download-a-file-using-window-fetch – clockwatcher Jun 19 '20 at 18:48

3 Answers3

0

You can do the below

return send_from_directory(dir, file_name, as_attachment=True)

This will download the file on the user's machine.

Edit:

BTW, if you create an html form like below, you do not need javascript.

<form action='action here' method='post'>
    <input type='submit'>
</form>
KetZoomer
  • 2,701
  • 3
  • 15
  • 43
  • I tried send_from_directory as well...it did not work. As for the HTML, the data I want to send is in a different section, so I cant get it inside the form – RishiC Jun 19 '20 at 17:18
  • Okay. Can you be more specific about what happened. What error shows. It can be helpful to do: app.run(debug=True) – KetZoomer Jun 19 '20 at 17:55
  • There wasnt any error, I clicked the button,and nothing happened – RishiC Jun 20 '20 at 03:40
0

As @clockwatcher mentioned a different question, I used the download.js module to handle the download of the image.

So my JS code looks like this now:

function make_image(text){
    const json={
        text: text
    };
    const options={
        method: "POST",
        body: JSON.stringify(json),
        headers:{
            'Content-Type':'application/json',
        }
    };

    fetch('/image',options)
        .then(res=>{
            return res.blob();
        }).then(blob=>{
            download(blob)
        }).catch(err=>console.log(err));
}

And an addition to the script tag in the html:

<script src="https://cdnjs.cloudflare.com/ajax/libs/downloadjs/1.4.8/download.min.js"></script>

With no change in the Python server code.

It works now

RishiC
  • 768
  • 1
  • 10
  • 34
0

The error in your code is in the Javascript The response being sent from your Python server is a file

return send_file(f"{name}.png",as_attachment=True,mimetype="image/png")

However you are converting the response (file as intended) to JSON usin the json() method

    ...

    fetch('/image',options)
        .then(res=>{
            res.json(); // **
        }).catch(err=>console.log(err));

What you would want to do instead will be to receive the file as a blob using the blob() method

EDIT In order to download automatically you will need to create an anchor element, define its download attribute and automate the click on it

In your JS

    ...

    fetch('/image',options)
        .then((res)=>{
            return res.blob()
        }).then((blob)=>{
           let el = document.createElement("a"); 
           // creates anchor element but doesn't add it to the DOM
           el.setAttribute("download", [filename]) 
           // make the link downloadable on click
           let url = URL.createObjectUrl(blob); 
           // creates a url to the retrieved file
           el.href = url; // set the href attribute attribute
           el.click(); 
        })
    // catch errors
    .catch(err=>console.log(err));

  • The uncaught syntax error is as a result of Javascript trying to convert your image to a json object but encountering image data that it cannot convert – David Timi Jun 26 '23 at 09:10
  • David, I tried this and it solved the json error, but "nothing happens" in terms of it downloading the file passed back to the front-end from Flask. I'm attempting to do this with a generated xlsx file. I'm not sure how to actually get it to download via a .fetch call. It works perfect if I visit the endpoint directly in my browser though. – Source Matters Aug 25 '23 at 04:35
  • The [filename] can be passed as a parameter if not set to ""(empty string) – David Timi Aug 26 '23 at 09:00