Issue
I am developing a webhook in which a third-party service will hit my URL and will provide some files, now I can not use FastAPI's UploadFile = File (...)
because it throws an error of the required field File
I want to read the payload and files from the request object as we can do in Flask by simply doing this
from flask import request
files = request.files
How can I achieve the same in FastAPI?
Solution
You could use Starlette's request.form()
method to parse the body, which would return a FormData
object containing both file uploads and text input. For more details and references, please have a look at this answer, on which the following example is based. Another solution would be to use the approach demonstrated in Option 1 of this answer.
Also, if, for any reason, you would like to use a def
instead of async def
endpoint, please have a look at this answer on how to read the file contents inside a def
endpoint. You might find this answer helpful as well.
Working Example
app.py
from fastapi import FastAPI, Depends, Request, HTTPException
from starlette.datastructures import FormData
app = FastAPI()
async def get_body(request: Request):
content_type = request.headers.get('Content-Type')
if content_type is None:
raise HTTPException(status_code=400, detail='No Content-Type provided!')
elif (content_type == 'application/x-www-form-urlencoded' or
content_type.startswith('multipart/form-data')):
try:
return await request.form()
except Exception:
raise HTTPException(status_code=400, detail='Invalid Form data')
else:
raise HTTPException(status_code=400, detail='Content-Type not supported!')
@app.post('/')
async def main(body = Depends(get_body)):
if isinstance(body, FormData): # if Form/File data received
msg = body.get('msg')
items = body.getlist('items')
files = body.getlist('files') # returns a list of UploadFile objects
if files:
for file in files:
print(f'Filename: {file.filename}. Content (first 15 bytes): {await file.read(15)}')
return 'OK'
test.py
import requests
url = 'http://127.0.0.1:8000/'
data = {'items': ['foo', 'bar'], 'msg': 'Hello!'}
files = [('files', open('a.txt', 'rb')), ('files', open('b.txt', 'rb'))]
# Send Form data and files
r = requests.post(url, data=data, files=files)
print(r.text)
# Send Form data only
r = requests.post(url, data=data)
print(r.text)
Answered By - Chris
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.