Search code examples
pythonpython-3.xfastapiuvicornstarlette

How to immediately return uploaded file in FastAPI or Starlette?


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


Solution

  • 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")