FastAPI – Uploading multiple files using Axios raises Bad Request error

Question:

Client code:

!<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<form id="uploadForm" role="form" method="post" enctype=multipart/form-data>
    <input type="file" id="file" name="file" multiple>
    <input type=button value=Upload onclick="uploadFile()">
</form>

<script type="text/javascript">
function uploadFile() {
   var formData = new FormData();
    var imagefile = document.querySelector('#file');
    formData.append("images", imagefile.files);
    axios.post('http://127.0.0.1:8000/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        }
    })
}
</script>
</body>
</html>

Server code:

from fastapi import FastAPI, File, UploadFile, FastAPI
from typing import Optional, List
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware

...

def save_file(filename, data):
    with open(filename, 'wb') as f:
        f.write(data)
        print('file saved')

@app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
    print(files)
    for file in files:
        contents = await file.read()
        save_file(file.filename, contents)
        print('file received')

    return {"Uploaded Filenames": [file.filename for file in files]}

I get the following error:

 ←[32mINFO←[0m:     127.0.0.1:10406 - "←[1mPOST /upload HTTP/1.1←[0m" ←[31m400 Bad Request←[0m 

I have tried to upload a single file via form action and all works fine, but I need to upload two files.

Asked By: Dmitry Sokolov

||

Answers:

First, when uploading files or form data, one should use the same form key defined in their endpoint/route. In your case, that is files. Hence, on client side you should use that key instead of images.

Second, the way to upload multiple files is to loop through the array of files (i.e., imagefile.files in your code) and add each file to the FormData object.

Third, the error seems to occur due to some bug/changes in the 0.27.1 version of Axios. Here is a recent related issue on GitHub. Using the latest version, i.e., 0.27.2, from cdnjs here resolves the issue.

Alternatively, you could use Fetch API, similar to this answer (you can add the list of files in the same way as shown in the Axios example below). In Fetch API, when uploading files to the server, you should not explicitly set the Content-Type header on the request, as explained here.

Working Example (using Axios)

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>Upload Files</title>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>   
   </head>
   <body>
      <input type="file" id="fileInput" multiple><br>
      <input type="button" value="Upload" onclick="uploadFile()">
      <script type="text/javascript">
         function uploadFile() {
             var fileInput = document.querySelector('#fileInput'); 
         
             if (fileInput.files[0]) {
                var formData = new FormData();
                for (const file of fileInput.files)
                    formData.append('files', file);

                 axios({
                         method: 'post',
                         url: '/upload',
                         data: formData,
                         headers: {
                             'Accept': 'application/json',
                             'Content-Type': 'multipart/form-data'
                         }
                     })
                    .then(response => {
                        console.log(response);
                    })
                    .catch(error => {
                        console.error(error);
                    });
             }
         }
      </script>
   </body>
</html>
Answered By: Chris

Starting from Axios v 0.27.2 you can do this easily:

axios
    .postForm("https://httpbin.org/post", document.querySelector("#fileInput").files)

All the files will be submitted with files[] key.

More verbose example:

    axios.postForm("https://httpbin.org/post", {
      "myField": "foo"
      "myJson{}": {x:1, y: 'bar'}, 
      "files[]": document.querySelector("#fileInput").files
    })
Answered By: Dmitriy Mozgovoy
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.