如何在fastAPI中返回图像?

时间:2019-04-26 18:29:36

标签: python api fastapi

使用python模块fastAPI,我不知道如何返回图像。在烧瓶中,我将执行以下操作:

@app.route("/vector_image", methods=["POST"])
def image_endpoint():
    # img = ... # Create the image here
    return Response(img, mimetype="image/png")

此模块中对应的调用是什么?

7 个答案:

答案 0 :(得分:25)

我有一个类似的问题,但是带有cv2图像。这可能对其他人有用。使用StreamingResponse

import io
from starlette.responses import StreamingResponse

app = FastAPI()

@app.post("/vector_image")
def image_endpoint(*, vector):
    # Returns a cv2 image array from the document vector
    cv2img = my_function(vector)
    res, im_png = cv2.imencode(".png", cv2img)
    return StreamingResponse(io.BytesIO(im_png.tobytes()), media_type="image/png")

答案 1 :(得分:8)

所有其他答案都是正确的,但是现在返回图像非常容易

from fastapi.responses import FileResponse

@app.get("/")
async def main():
    return FileResponse("your_image.jpeg")

答案 2 :(得分:6)

感谢@biophetik的回答,并有一个引起我困惑的重要提示:如果您正在使用BytesIO,尤其是PIL / skimage,请确保在返回前也执行img.seek(0)

@app.get("/generate")
def generate(data: str):
  img = generate_image(data)
  print('img=%s' % (img.shape,))
  buf = BytesIO()
  imsave(buf, img, format='JPEG', quality=100)
  buf.seek(0) # important here!
  return StreamingResponse(buf, media_type="image/jpeg",
    headers={'Content-Disposition': 'inline; filename="%s.jpg"' %(data,)})

答案 3 :(得分:4)

如果您已经在内存中拥有图像的字节数,请使用您的自定义 fastapi.responses.Responsecontent 返回 media_type

您还需要使用端点装饰器来让 FastAPI 将正确的媒体类型放入 OpenAPI 规范中。

@app.get(
    "/image",

    # Prevent FastAPI from adding "application/json" as a possible
    # response media type in the autogenerated OpenAPI specification.
    # https://github.com/tiangolo/fastapi/issues/3258
    response_class=Response,

    # Set what the media type will be in the autogenerated OpenAPI specification.
    # fastapi.tiangolo.com/advanced/additional-responses/#additional-media-types-for-the-main-response
    responses = {
        200: {
            "content": {"image/png": {}}
        }
    }
)
def get_image()
    image_bytes: bytes = generate_cat_picture()
    # media_type here sets the media type of the actual response sent to the client.
    return Response(content=image_bytes, media_type="image/png")

如果您的图像仅存在于文件系统中,请改为返回 fastapi.responses.FileResponse

请参阅 FastAPI 文档中的 Custom Response 部分。


其他答案建议StreamingResponse。除非您确定不能使用 ResponseFileResponse,否则我不建议这样做,因为 StreamingResponse 更难正确使用。

特别是,这样的代码毫无意义。它不会以任何有用的方式“流式传输”图像。

@app.get("/image")
def get_image()
    image_bytes: bytes = generate_cat_picture()
    # ❌ Don't do this.
    image_stream = io.BytesIO(image_bytes)
    return StreamingResponse(content=image_stream, media_type="image/png")

首先,StreamingResponse(content=my_iterable) 通过迭代 my_iterable 提供的块来进行流式处理。但是当该可迭代对象是 BytesIOthe chunks will be \n-terminated lines 时,这对二进制图像没有意义。

即使分块有意义,分块在这里也毫无意义,因为我们从一开始就拥有整个 image_bytes bytes 对象。我们也可以从一开始就将整个内容传递到 Response 中。通过阻止来自 FastAPI 的数据,我们不会获得任何好处。

其次,StreamingResponse 对应于 HTTP chunked transfer encoding。 (这可能取决于您的 ASGI 服务器,但至少是 Uvicorn 的情况。)这不是分块传输编码的良好用例。

当您提前不知道输出的大小,并且您不想等待收集所有信息后才开始将其发送到客户端时,分块传输编码是有意义的。这适用于提供慢速数据库查询结果等内容,但通常不适用于提供图像。

不必要的分块传输编码可能有害。例如,这意味着客户端在下载文件时不能有进度条。见:

答案 4 :(得分:1)

该文件尚未正确记录,但是您可以使用Starlette中的任何内容。

因此,如果它是磁盘中具有以下路径的文件:https://www.starlette.io/responses/#fileresponse

,则可以使用FileResponse

如果它是在 path操作中创建的类似文件的对象,则在Starlette的下一个稳定版本(由FastAPI内部使用)中,您还可以通过{{1} }。

答案 5 :(得分:0)

@SebastiánRamírez的answer为我指明了正确的方向,但是对于那些希望解决问题的人来说,我需要几行代码才能使其工作。我需要ed从starlette(不是fastAPI?)导入FileResponse,添加CORS支持,并从临时文件返回。也许有更好的方法,但是我无法使流工作:

from starlette.responses import FileResponse

app = FastAPI()
app.add_middleware(
    CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
)

def image_endpoint(*, item: Item):
    # Returns a raw PNG from the document vector

    with tempfile.NamedTemporaryFile(mode="w+b", suffix=".png", delete=False) as FOUT:
        FOUT.write(img)
        return FileResponse(FOUT.name, media_type="image/png")


答案 6 :(得分:0)

您可以在FastAPI中执行非常相似的操作

// Get the distinct file IDs that correspond to each upload.
db.collection('media.chunks').distinct('files_id', function(error, results){
    console.log('Distinct file ID results',results);
    return res.status(200).send();
});