使用 python 模块fastAPI,我不知道如何返回图像。在烧瓶中,我会做这样的事情:
@app.route("/vector_image", methods=["POST"])
def image_endpoint():
# img = ... # Create the image here
return Response(img, mimetype="image/png")
这个模块中对应的调用是什么?
我有一个类似的问题,但有一个 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")
fastapi.responses.Response
使用您的自定义返回 acontent
和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
文档。
返回一个fastapi.responses.FileResponse
。
请参阅FileResponse
文档。
StreamingResponse
其他答案建议StreamingResponse
。StreamingResponse
更难正确使用,所以我不推荐它,除非你确定你不能使用Response
or FileResponse
。
特别是,这样的代码毫无意义。它不会以任何有用的方式“流式传输”图像。
@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
. 但是当该可迭代对象是 a 时BytesIO
,块将是\n
-terminated lines,这对于二进制图像没有意义。
即使分块是有意义的,分块在这里也毫无意义,因为我们image_bytes
bytes
从一开始就拥有了整个对象。我们还不如Response
从一开始就将整个事情传递给a。通过保留来自 FastAPI 的数据,我们不会获得任何好处。
其次,StreamingResponse
对应于HTTP 分块传输编码。(这可能取决于您的 ASGI 服务器,但至少是Uvicorn的情况。)这不是分块传输编码的好用例。
当您不提前知道输出的大小并且不想在开始将其发送到客户端之前收集所有内容以找出答案时,分块传输编码是有意义的。这可以适用于提供慢速数据库查询的结果之类的东西,但它通常不适用于提供图像。
不必要的分块传输编码可能是有害的。例如,这意味着客户端在下载文件时无法显示进度条。看:
它尚未正确记录,但您可以使用 Starlette 中的任何内容。
FileResponse
因此,如果它是磁盘中带有路径的文件,您可以使用: https ://www.starlette.io/responses/#fileresponse
如果它是在您的路径操作中创建的类似文件的对象,那么在 Starlette 的下一个稳定版本(由 FastAPI 内部使用)中,您还可以在StreamingResponse
.
所有其他答案都很重要,但现在返回图像非常容易
from fastapi.responses import FileResponse
@app.get("/")
async def main():
return FileResponse("your_image.jpeg")
感谢@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,)})
@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")
你可以在 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")
FileResponse
如果它是磁盘中的文件,则可以使用a 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!"}
由于我的图像是用 PIL 构建的,因此我的需求并没有完全满足。我的 fastapi 端点获取一个图像文件名,将其作为 PIL 图像读取,并在内存中生成一个缩略图 jpeg,可以在 HTML 中使用,例如:
<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")