I am using FastAPI to upload file according to the official documentation, just like:
@app.post("/create_file/")
async def create_file(file: UploadFile=File(...)):
file2store = await file.read()
# some code to store the BytesIO(file2store) to the other database
When I send a request using Python requests lib:
f = open(".../file.txt", 'rb')
files = {"file": (f.name, f, "multipart/form-data")}
requests.post(url="SERVER_URL/create_file", files=files)
the file2store is always empty. Sometimes (rarely seen), it can get the file bytes, but almost all the time it is empty, so I can't restore the file on the other database.
I also tried the bytes rather than UploadFile, I get the same results.
Is there something wrong in my code, or is the way I use the FastAPI to upload file wrong? I googled it for some answers for a long time, but failed. So I raise the problem here, hoping someone who knows the answer can help me out.
Note that the answer below uses synchronous writing for writing the files to disk. If you need async writing, please have a look at this answer. Moreover, if you need to send additional data (such as JSON data) together with uploading the file(s), please have a look at this answer. I would suggest you have a look at this answer as well, which explains the difference between defining an endpoint with def and async def, and demonstrates how to read the contents of an uploaded file when using def (if that's a requirement for your app).
app.py
import uvicorn
from fastapi import File, UploadFile, FastAPI
app = FastAPI()
@app.post("/upload")
async def upload(file: UploadFile = File(...)):
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"}
finally:
await file.close()
return {"message": f"Successfuly uploaded {file.filename}"}
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8000)
test.py
import requests
url = 'http://127.0.0.1:8000/upload'
file = {'file': open('images/1.png', 'rb')}
resp = requests.post(url=url, files=file)
print(resp.json())
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
url = 'http://127.0.0.1:8000/upload'
files = [('files', open('images/1.png', 'rb')), ('files', open('images/2.png', 'rb'))]
resp = requests.post(url=url, files=files)
print(resp.json())
@app.post("/create_file/")
async def image(image: UploadFile = File(...)):
print(image.file)
# print('../'+os.path.isdir(os.getcwd()+"images"),"*************")
try:
os.mkdir("images")
print(os.getcwd())
except Exception as e:
print(e)
file_name = os.getcwd()+"/images/"+image.filename.replace(" ", "-")
with open(file_name,'wb+') as f:
f.write(image.file.read())
f.close()
file = jsonable_encoder({"imagePath":file_name})
new_image = await add_image(file)
return {"filename": new_image}
I had worked on it nearly 7-8 months ago, I didn't encounter the problem which you are saying. I had found out that that UploadFile is of type SpooledTemproaryFile and I didn't try reading file asynchronously. Here is the snippet which is being used. I have uploaded all types of files up to 100mb, it is working. I had to save file on server on a specific path
with open(path, 'wb') as f:
[f.write(chunk) for chunk in iter(lambda: file.file.read(10000), b'')]
In your case I'd suggest reading file and making the post request directly using aiohttp sessions or AsyncClient from httpx