Upload single image from axios to FastAPI: "Expected UploadFile, received: <class 'str'>"
Asked Answered
P

1

4

I try to upload an image from my react-admin app to FastAPI using axios. The ImageInput component returns a File object which I cast to a Blob and try to upload using axios.
The API client I am using has been generated by orval.

The response I receive after sending the POST:

{
    "detail":[
        {
            "loc":[
                "body",
                "file"
            ],
            "msg":"Expected UploadFile, received: <class 'str'>",
            "type":"value_error"
        }
    ]
}

axios request function:

/**
 * @summary Create Image
 */
export const createImage = (
  bodyCreateImageImagesPost: BodyCreateImageImagesPost,
  options?: AxiosRequestConfig
): Promise<AxiosResponse<Image>> => {
  const formData = new FormData();
  formData.append(
    "classified_id",
    bodyCreateImageImagesPost.classified_id.toString()
  );
  formData.append("file", bodyCreateImageImagesPost.file);

  return axios.post(`/images`, formData, options);
};

axios request headers:

POST /images HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: application/json, text/plain, */*
Accept-Language: pl,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Authorization: bearer xxx
Content-Type: multipart/form-data; boundary=---------------------------41197619542060894471320873154
Content-Length: 305
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Referer: http://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-GPC: 1

Request data object:

{
  "classified_id": 2,
  "file": {
    "rawFile": {...},
    "src": "blob:http://localhost:3000/9826efb4-875d-42f9-9554-49a6b13204be",
    "name": "Screenshot_2019-10-16-18-04-03.png"
  }
}

FastAPI endpoint:

def create_image(
    classified_id: int = Form(...),
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
    user: User = Security(manager, scopes=["images_create"]),
) -> Any:
    # ...

In the "Network" section of the developer tools in a browser, it shows the file field as [object Object] but I guess that's just a problem with no string representation of the Blob?

When I try to upload an image through the Swagger UI, it works as expected and the curl request looks like this:

curl -X 'POST' \
  'http://localhost:8000/images' \
  -H 'accept: application/json' \
  -H 'content-length: 3099363' \
  -H 'Authorization: Bearer xxx' \
  -H 'Content-Type: multipart/form-data' \
  -F 'classified_id=2' \
  -F 'file=@Screenshot_2019-10-16-18-04-03.png;type=image/png'

Any ideas what is wrong in here? How should the proper axios request look like?

Pathos answered 23/1, 2022 at 16:11 Comment(0)
S
3

Use as follows (remember to change the url, as well as the Accept header, according to your FastAPI endpoint):

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
<script type="text/javascript">
function uploadFile() {
    var formData = new FormData();
    var fileInput = document.getElementById('fileInput');
    if (fileInput.files[0]) {
        formData.append("classified_id", 2);
        formData.append("file", fileInput.files[0]);
        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>
<input type="file" id="fileInput" name="file"><br>
<input type="button" value="submit" onclick="uploadFile()">
Smew answered 23/1, 2022 at 16:41 Comment(4)
I've changed the file parameter of the query to the File type instead of Blob and added the Content-Type header as in your code but nothing changed. I can't see any other differences comparing to my code provided.Pathos
Yes, after I've added the Authorization header (btw. you have a typo in the headers property name) and used your code for input it works as expected. The problem is that the framework I am using (react-admin) does not return files as the files property of the input but as File objects which are not available by checking the property mentioned as they are dynamically changed to the img elements with the src attributes containing blob like: blob:http://localhost:3000/aece2967-4f9a-4c18-acb0-ad9aac1c8336.Pathos
I don't have an idea how to get it working with the functionality that the framework mentioned provides. I can see that when using your code, in the request body there is actual content of the image instead of the [object Object] string.Pathos
I didn't, but your suggestion gave me an idea what can be an issue in this case. I've checked the type of the Object that the react-admin gives me and it turned out that the property of it (rawFile) is the actual File object - it's not the main object as I thought. After changing the type of the file parameter in orval-generated interface it works! Thanks Chris.Pathos

© 2022 - 2024 — McMap. All rights reserved.