'Requests format for uploading multiple images in FastAPI

Example

Here's my code trying to upload a list of images:

import requests
import glob
import cv2

path = glob.glob("test_folder/*", recursive=True) # a list of image's path

lst_img = []
for p in path[:3]:
    # img = cv2.imread(p)
    lst_img.append((p, open(p, 'rb'), "image/jpeg"))

data = {"files": lst_img}

url = "http://localhost:6789/" # url api of app

res = requests.post(url=url, data=data)

print(res.status_code)

print(res.text)

Description

I am trying to upload a list of images through Python requests (package) to a FastAPI endpoint, but maybe my request's format is wrong, leading to a 422 error:

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

This is my request's format:

{'files': [('test_folder/image77.jpeg', <_io.BufferedReader name='test_folder/image77.jpeg'>, 'image/jpeg'), ('test_folder/image84.jpeg', <_io.BufferedReader name='test_folder/image84.jpeg'>, 'image/jpeg'), ('test_folder/image82.jpeg', <_io.BufferedReader name='test_folder/image82.jpeg'>, 'image/jpeg')]}

I've tried many ways, but always fails. Many thank if u guys help to solve it.

Environment

  • OS: Linux: (Ubuntu 18.04)
  • FastAPI Version: 0.61.1
  • Requests Version: 2.24.0
  • Python Version: 3.7.5

I tried the below, but still not working:

lst_img.append(("file", (p, open(p, 'rb'), "image/jpeg")))

My FastAPI main.py

from typing import List

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import StreamingResponse, FileResponse

app = FastAPI()

@app.post("/")
async def main(files: List[UploadFile] = File(...)):
    # file_like = open(video_path, mode="rb")
    # return StreamingResponse(file_like, media_type="video/mp4")
    return {"filenames": [file.filename for file in files]}


Solution 1:[1]

Below is an example of how to upload multiple files (images) using Python requests and FastAPI. If you need to send additional data when uploading the files, please have a look here. Also, if you need async writing for saving the images on server side, please have a look at this answer.

app.py

import uvicorn
from fastapi import File, UploadFile, FastAPI
from typing import List

app = FastAPI()

@app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
    for file in files:
        try:
            contents = await file.read()
            with open(file.filename, 'wb') as f:
                f.write(contents)
        except Exception:
            return {"message": "There was an error uploading the file(s)"}
        finally:
            await file.close()

    return {"message": f"Successfuly uploaded {[file.filename for file in files]}"}    

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)

test.py

import requests
import glob

paths = glob.glob("images/*", recursive=True) # returns a list of file paths
images = [('files', open(p, 'rb')) for p in paths] # or paths[:3] to select the first 3 images
url = 'http://127.0.0.1:8000/upload'
resp = requests.post(url=url, files=images) 
print(resp.json())

Solution 2:[2]

You should be using the files argument for request module to send a file

import requests
import glob

path = glob.glob("test_folder/*", recursive=True) # a list of image's path

lst_img = []
for p in path[:3]:
    lst_img.append({"files": open(p, 'rb')})

url = "http://localhost:6789/" # url api of app

for data in lst_img:
    res = requests.post(url=url, files=data)
    print(res.status_code)
    print(res.text)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 clamentjohn