I want to upload a file and in request and in response to this request I want to return the same file.
I would prefer to do this without saving file to disk. So I want to somehow return SpooledTemporaryFile
content which is how FastAPI / Starlette stores in-memory uploaded file (with some smart disk caching).
I tried:
import uvicorn
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def upload_form():
return HTMLResponse("""
<form action="/upload/" method="post" enctype="multipart/form-data">
Choose file:
<input type="file" name="file" id="file">
<input type="submit" value="Send file" name="submit">
""")
@app.post("/upload/", response_class=StreamingResponse)
async def upload(file: UploadFile):
return StreamingResponse(file.file)
if __name__ == "__main__":
uvicorn.run("main:app")
But it doesn't work. When I upload a file, server crashes with an error:
TypeError: 'SpooledTemporaryFile' object is not an iterator
This question is similar to: https://www.reddit.com/r/FastAPI/comments/kxuukb/spooledfile_as_streamingresponse/
It may be related to: https://bugs.python.org/issue26175
It turned out Response
is needed for this use case instead of StreamingResponse
:
import uvicorn
from fastapi.responses import HTMLResponse, Response
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def upload_form():
return HTMLResponse("""
<form action="/upload/" method="post" enctype="multipart/form-data">
Choose file:
<input type="file" name="file" id="file">
<input type="submit" value="Send file" name="submit">
""")
@app.post("/upload/", response_class=Response)
async def upload(file: UploadFile):
return Response(file.file.read(), media_type=file.content_type)
if __name__ == "__main__":
uvicorn.run("main_stack:app")