3

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?

szelbi
  • 67
  • 4
  • 14

1 Answers1

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()">
Chris
  • 18,724
  • 6
  • 46
  • 80
  • 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. – szelbi Jan 23 '22 at 16:57
  • 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`. – szelbi Jan 23 '22 at 17:28
  • 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. – szelbi Jan 23 '22 at 17:29
  • 2
    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. – szelbi Jan 23 '22 at 18:02