代码之家  ›  专栏  ›  技术社区  ›  Hooked

如何在fastAPI中返回图像?

  •  0
  • Hooked  · 技术社区  · 7 年前

    使用python模块 fastAPI ,我不知道如何返回图像。在烧瓶里,我会这样做:

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

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

    0 回复  |  直到 7 年前
        1
  •  54
  •   biophetik    6 年前

    我也有类似的问题,但有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")
    
        2
  •  37
  •   Maxpm    5 年前

    如果内存中已经有图像的字节

    归还 fastapi.responses.Response 按照你的习惯 content media_type .

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

    @app.get(
        "/image",
    
        # 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": {}}
            }
        }
    
        # Prevent FastAPI from adding "application/json" as an additional
        # response media type in the autogenerated OpenAPI specification.
        # https://github.com/tiangolo/fastapi/issues/3258
        response_class=Response,
    )
    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")
    

    看到了吗 Response documentation .

    如果您的映像只存在于文件系统中

    归还 fastapi.responses.FileResponse .

    看到了吗 FileResponse documentation .


    小心 StreamingResponse

    其他答案表明 StreamingResponse . 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 .但当它是一个 BytesIO , the chunks will be \n -terminated lines ,这对二值图像没有意义。

    即使分块是有意义的,分块在这里也是毫无意义的,因为我们有整个 image_bytes bytes 对象从一开始就可用。我们还不如把整件事放到一个 回答 从一开始。我们从FastAPI中保留数据不会得到任何好处。

    第二 StreamingResponse 对应于 HTTP chunked transfer encoding (这可能取决于您的ASGI服务器,但对于 Uvicorn (至少如此。)对于分块传输编码来说,这不是一个好的用例。

    如果您事先不知道输出的大小,并且不想等到收集完之后再开始将其发送到客户端,分块传输编码就有意义了。这可以应用于像服务慢速数据库查询的结果这样的事情,但通常不适用于服务图像。

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

        3
  •  31
  •   tiangolo    7 年前

    它还没有正确的文档记录,但你可以使用Starlette的任何东西。

    所以,你可以使用 FileResponse 如果是磁盘中具有路径的文件: https://www.starlette.io/responses/#fileresponse

    如果它是在你的 路径操作 ,在Starlette的下一个稳定版本(FastAPI内部使用)中,您还可以在 StreamingResponse .

        4
  •  29
  •   Yagiz Degirmenci    5 年前

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

    from fastapi.responses import FileResponse
    
    @app.get("/")
    async def main():
        return FileResponse("your_image.jpeg")
    
        5
  •  13
  •   Hendy Irawan    6 年前

    感谢@biophetik的回答,还有一个让我困惑的重要提醒: 如果你正在使用 BytesIO 尤其是PIL/撇渣时,确保也这样做 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,)})
    
        6
  •  12
  •   Davide Fiocco    6 年前

    这个 answer 来自@SebastiÃ…nRamÃrez的文章为我指明了正确的方向,但对于那些希望解决这个问题的人来说,我需要几行代码来让它工作。我需要进口 FileResponse 来自starlette(不是fastAPI?),添加CORS支持,并从临时文件返回。也许有更好的方法,但我无法开始工作:

    from starlette.responses import FileResponse
    from starlette.middleware.cors import CORSMiddleware
    import tempfile
    
    app = FastAPI()
    app.add_middleware(
        CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
    )
    
    @app.post("/vector_image")
    def image_endpoint(*, vector):
        # Returns a raw PNG from the document vector (define here)
        img = my_function(vector)
    
        with tempfile.NamedTemporaryFile(mode="w+b", suffix=".png", delete=False) as FOUT:
            FOUT.write(img)
            return FileResponse(FOUT.name, media_type="image/png")
    
        7
  •  2
  •   Jibin Mathew    5 年前

    您可以在FastAPI中执行类似的操作

    from fastapi import FastAPI, Response
    
    app = FastAPI()
    
    @app.post("/vector_image/")
    async def image_endpoint():
        # img = ... # Create the image here
        return Response(content=img, media_type="image/png")
    
        8
  •  1
  •   Milovan TomaÅ¡ević    4 年前

    你可以用 FileResponse 如果它是磁盘中带有 path :

    import os
    
    from fastapi import FastAPI 
    from fastapi.responses import FileResponse
    
    app = FastAPI()
    
    path = "/path/to/files"
    
    @app.get("/")
    def index():
        return {"Hello": "World"}
    
    @app.get("/vector_image", responses={200: {"description": "A picture of a vector image.", "content" : {"image/jpeg" : {"example" : "No example available. Just imagine a picture of a vector image."}}}})
    def image_endpoint():
        file_path = os.path.join(path, "files/vector_image.jpg")
        if os.path.exists(file_path):
            return FileResponse(file_path, media_type="image/jpeg", filename="vector_image_for_you.jpg")
        return {"error" : "File not found!"}
    
        9
  •  1
  •   dam    4 年前

    从上面看,我的需求并没有完全满足,因为我的形象是用PIL建立的。我的fastapi端点采用图像文件名,将其读取为PIL图像,并在内存中生成可在HTML中使用的缩略图jpeg,如:

    <img src="http://localhost:8000/images/thumbnail/bigimage.jpg">

    import io
    from PIL import Image
    from fastapi.responses import StreamingResponse
    @app.get('/images/thumbnail/{filename}',
      response_description="Returns a thumbnail image from a larger image",
      response_class="StreamingResponse",
      responses= {200: {"description": "an image", "content": {"image/jpeg": {}}}})
    def thumbnail_image (filename: str):
      # read the high-res image file
      image = Image.open(filename)
      # create a thumbnail image
      image.thumbnail((100, 100))
      imgio = io.BytesIO()
      image.save(imgio, 'JPEG')
      imgio.seek(0)
      return StreamingResponse(content=imgio, media_type="image/jpeg")