I have a FastAPI endpoint that is generating PIL images. I want to then send the resulting image as a stream to a Jinja2 TemplateResponse
. This is a simplified version of what I am doing:
import io
from PIL import Image
@api.get("/test_image", status_code=status.HTTP_200_OK)
def test_image(request: Request):
'''test displaying an image from a stream.
'''
test_img = Image.new('RGBA', (300,300), (0, 255, 0, 0))
# I've tried with and without this:
test_img = test_img.convert("RGB")
test_img = test_img.tobytes()
base64_encoded_image = base64.b64encode(test_img).decode("utf-8")
return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})
With this simple html:
<html>
<head>
<title>Display Uploaded Image</title>
</head>
<body>
<h1>My Image<h1>
<img src="data:image/jpeg;base64,{{ myImage | safe }}">
</body>
</html>
I've been working from these answers and have tried multiple permutations of these:
How to display uploaded image in HTML page using FastAPI & Jinja2?
How to convert PIL Image.image object to base64 string?
How can I display PIL image to html with render_template flask?
This seems like it ought to be very simple but all I get is the html icon for an image that didn't render.
What am I doing wrong? Thank you.
I used Mark Setchell's answer, which clearly shows what I was doing wrong, but still am not getting an image in html. My FastAPI is:
@api.get("/test_image", status_code=status.HTTP_200_OK)
def test_image(request: Request):
# Create image
im = Image.new('RGB',(1000,1000),'red')
im.save('red.png')
print(im.tobytes())
# Create buffer
buffer = io.BytesIO()
# Tell PIL to save as PNG into buffer
im.save(buffer, 'PNG')
# get the PNG-encoded image from buffer
PNG = buffer.getvalue()
print()
print(PNG)
base64_encoded_image = base64.b64encode(PNG)
return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})
and my html:
<html>
<head>
<title>Display Uploaded Image</title>
</head>
<body>
<h1>My Image 3<h1>
<img src="data:image/png;base64,{{ myImage | safe }}">
</body>
</html>
When I run, if I generate a 1x1 image I get the exact printouts in Mark's answer. If I run this version, with 1000x1000 image, it saves a red.png that I can open and see. But in the end, the html page has the heading and the icon for no image rendered. I'm clearly doing something wrong now in how I send to html.