4

I'm trying to upload a file from React frontend to FastAPI backend. The code I used is as shown below.

This is the FastAPI backend:

@app.post("/uploadfile")
async def create_upload_file(file: UploadFile = File(...)):
    return {"filename": file.filename}

This is the frontend:

const [file, uploadFile] = useState(null)

//when upload button clicked
function handleSubmit(){
    console.log(file[0].name)
    const formdata = new FormData();
    formdata.append(
      "file",
      file[0],
    )
    axios.post("/uploadfile", {
      file:formdata}, {
        "Content-Type": "multipart/form-data",
      })
          .then(function (response) {
            console.log(response); //"dear user, please check etc..."
          });
      
  }

// this is when file has been selected
  function handleChange(e){
    uploadFile(e.target.files); //store uploaded file in "file" variable with useState
  }

It returns a 422 (Unprocessable Entity). Here's the message detail from axios:

enter image description here

I am not quite familiar with the rules and format needed behind file uploading. Could someone clear my confusion?

1
  • Related answers can be found here, as well as here and here Commented Jul 27, 2023 at 4:47

3 Answers 3

5

This was helpful for me finding a bug in my code. I suspect your problem was nesting the formData inside another object in your first attempt: axios.post("/uploadfile", {file:formdata} ... should be axios.post("/uploadfile", formdata ....

For me, the trick was ensuring that the key to the formData.append exactly matches the name of the UploadFile parameter for my FastAPI endpoint.

In this example the name is file123.

In the React code you have:

formdata.append("file123", file[0])
axios.post("/uploadfile", formdata ...etc...

And in the Python code you have:

async def create_upload_file(file123: UploadFile = File(...)): ...etc...
Sign up to request clarification or add additional context in comments.

Comments

1

Update from OP: I have managed to solve the problem by replacing the axios part with

const headers={'Content-Type': file[0].type}
await axios.post("/uploadfile",formdata,headers)
    .then()//etc

if anyone want to add more information on why that works please feel free to do so - since I'm not quite sure either.

2 Comments

This was helpful for me. I think the key is ensuring that the key to the form data matches the UploadFIle parameter for you FastAPI endpoint. In this case the key is file. In the react code you have: formdata.append( "file", file[0], ) And in the Python code you have: ``` async def create_upload_file(file: UploadFile = File(...)): ```
Axios will replace the content-type header with the appropriate values for the FormData instance. There is no need to manually construct it
0

Here are two options for uploading a file to a FastAPI application, using JavaScript in the frontend. Option 1 demonstrates a Fetch API approach, while Option 2 uses the Axios HTTP client.

If one would like to upload the file through an HTML <form> instead, please have a look at Method 1 of this answer. For multiple files uploading, this, as well this and this demonstrate on how to achieve that, using either fetch or axios. For a faster uploading file approach than using UploadFile—especially, when dealing with uploading rather large files in size—please take a look at the "Update" section of this answer.

Working Example

The FastAPI backend should be the same for both options below and is as follows:

app.py

from fastapi import File, UploadFile, Request, FastAPI, HTTPException
from fastapi.templating import Jinja2Templates
import aiofiles

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/upload")
async def upload(file: UploadFile = File(...)):
    try:
        contents = await file.read()
        async with aiofiles.open(file.filename, 'wb') as f:
            await f.write(contents)
    except Exception:
        raise HTTPException(status_code=500, detail='Something went wrong')
    finally:
        await file.close()

    return {"message": f"Successfuly uploaded {file.filename}"}


@app.get("/")
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Option 1 (using fetch)

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>Upload File</title>
   </head>
   <body>
      <input type="file" id="fileInput"><br>
      <input type="button" value="Upload" onclick="uploadFile()">
      <script type="text/javascript">
         function uploadFile() {
            var file = document.getElementById('fileInput').files[0];
         
            if (file) {
               var formData = new FormData();
               formData.append('file', file);
         
               fetch('/upload', {
                     method: 'POST',
                     body: formData,
                  })
                  .then(response => {
                     console.log(response);
                  })
                  .catch(error => {
                     console.error(error);
                  });
            }
         }
      </script>
   </body>
</html>

Option 2 (using axios)

CDN links for axios, if needed, could be found on either the official Axios website or CDNJS.

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>Upload File</title>
      <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>   
   </head>
   <body>
      <input type="file" id="fileInput"><br>
      <input type="button" value="Upload" onclick="uploadFile()">
      <script type="text/javascript">
         function uploadFile() {
            var file = document.getElementById('fileInput').files[0];
         
            if (file) {
               var formData = new FormData();
               formData.append('file', 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>

The below should work as well:

axios.post('/upload', formData, {
      headers: {
         'Content-Type': 'multipart/form-data'
      }
   })
   .then((response) => {
      console.log(response);
   })
   .catch((error) => {
      console.log(error);
   });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.