Search code examples
javascriptpythonflaskfrontendbackend

Upload file from html <input> to flask server with js


I'm trying to send a file from a JS frontend to a Flask backend. But my flask backend seems to recieve no file. It works when going on the flask backend HTML page and submiting manually, but I want the frontend to send the file to the backend.

Here's the javascript in question:

function send() {
    const formData = new FormData();
    const files = document.getElementById("file");
    formData.append("file", files.files[0]);
    const requestOptions = {
        headers: {
            "Content-Type": "multipart/form-data",
        },
        mode: "no-cors",
        method: "POST",
        files: files.files[0],
        body: formData,
    };
    console.log(requestOptions);

    fetch("http://localhost:5000/upload", requestOptions).then(
        (response) => {
            console.log(response.data);
        }
    );
}

export default function App() {
    return (
        <div className="App" id="App">
            <header className="App-header">
                <Button variant="outlined" component="label" size="large">
                    Load Audio
                    <input
                        id="file"
                        type="file"
                        onChange={send}
                        hidden
                    />
                </Button>
            </header>
        </div>
    );
}

Here's the flask python script for handling incoming requests:

@app.route("/upload", methods=["GET", "POST"])
def upload():
    if request.method == 'POST':
        if 'file' not in request.files:
            print(1)
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        if file.filename == '':
            print(2)
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            print(3)
            filename = secure_filename(file.filename)
            session["id"] = filename
            file.save(os.path.join('UPLOAD', filename))
            return redirect(url_for('uploaded',
                                    filename=filename))

    return render_template('upload.html')

It prints 1, so no file is found.

In case it's useful, Here's a debug with print(request.headers):

Host: localhost:5000
Connection: keep-alive
Sec-Ch-Ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Content-Type: multipart/form-data
Accept: */*
Origin: http://localhost:3000
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br

I read a lot online on how to uplaod file in that situation but i can't make it work. It would be great to have some help!


Solution

  • Your file handling in Flask seems just right. However, the way you specify your Content-Type header seems obscure to Flask.

    While running a simplified example of your code, I tried testing it with cURL in the following way:

    curl -XPOST http://localhost:5000/upload -F [email protected]
    

    This way Flask understood that a file has been uploaded, and I was able to see it in request.files:

    ipdb> request.files
    ImmutableMultiDict([('file', <FileStorage: 'TestFile.txt' ('text/plain')>)])
    

    What also brought my attention is that the Content-Type has a boundary specified:

    ('Content-Type', 'multipart/form-data; boundary=------------------------aa8098f7b7fa6c61')
    

    This makes me assume that specifying Content-Type: multipart/form-data manually doesn't seem to work.

    Since you are trying to do the upload via JS fetch, consider looking here. They also don't recommend setting the header manually.