Fast API - how to show an image from POST in GET?

Viewed 9947

I'm creating an app using FastAPI that is supposed to generate resized version of uploaded images. The upload should be done through POST/images and after calling a path /images/800x400 it should show a random image from the database with 800x400 size. I'm getting an error while trying to display an image.

 from fastapi.responses import FileResponse
 import uuid

 app = FastAPI()

 db = []

@app.post("/images/")
async def create_upload_file(file: UploadFile = File(...)):

    contents = await file.read() 

    db.append(file)

    with open(file.filename, "wb") as f:
        f.write(contents)

    return {"filename": file.filename}

@app.get("/images/")
async def show_image():  
     return db[0]

As a response I get:

{
  "filename": "70188bdc-923c-4bd3-be15-8e71966cab31.jpg",
  "content_type": "image/jpeg",
  "file": {}
}

I would like to use: return FileResponse(some_file_path) and in the file path put the filename from above. Is it right way of thinking?

1 Answers

First of all, you are adding the File object to your db list, that explains the response you get.

You want to write the content of the file to your db.

You also do not need to write it to the file system if you are using that as your "persistence" (of course all the files will go away if you shutdown or reload your app).

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import Response
import os
from random import randint
import uuid

app = FastAPI()

db = []


@app.post("/images/")
async def create_upload_file(file: UploadFile = File(...)):

    file.filename = f"{uuid.uuid4()}.jpg"
    contents = await file.read()  # <-- Important!

    db.append(contents)

    return {"filename": file.filename}


@app.get("/images/")
async def read_random_file():

    # get a random file from the image db
    random_index = randint(0, len(db) - 1)

    # return a response object directly as FileResponse expects a file-like object
    # and StreamingResponse expects an iterator/generator
    response = Response(content=db[random_index])

    return response

If you want to actually save the files to disk this is the method I would use (a real db is still preferred for a full application)

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import FileResponse
import os
from random import randint
import uuid

IMAGEDIR = "fastapi-images/"

app = FastAPI()


@app.post("/images/")
async def create_upload_file(file: UploadFile = File(...)):

    file.filename = f"{uuid.uuid4()}.jpg"
    contents = await file.read()  # <-- Important!

    # example of how you can save the file
    with open(f"{IMAGEDIR}{file.filename}", "wb") as f:
        f.write(contents)

    return {"filename": file.filename}


@app.get("/images/")
async def read_random_file():

    # get a random file from the image directory
    files = os.listdir(IMAGEDIR)
    random_index = randint(0, len(files) - 1)

    path = f"{IMAGEDIR}{files[random_index]}"
    
    # notice you can use FileResponse now because it expects a path
    return FileResponse(path)

Reference:

(FastAPI inherits responses from Starlette)

(Tiangolo's documentation is still very good to have)

Related