
Posted on • Originally published atgeekpython.in
Handling Images on the Frontend Using FastAPI
In this tutorial, we'll look at how to use FastAPI to display static and dynamic images on the frontend.
Displaying images on the frontend can be a time-consuming operation; it takes a significant amount of time and effort to create logic to show static and dynamic images. FastAPI includes certain classes and modules that can help you save time and effort when displaying images.
Displaying static images
If you've dealt with the Python Flask or Django web frameworks, you'll know that in order to serve static files, we need to include them in the static folder established within the project's root directory.
The procedure is the same, however, the logic for displaying static images in FastAPI differs.
The following code will help us to display thestatic images on the frontend.
# static_img.pyfromfastapiimportFastAPIfromfastapi.responsesimportHTMLResponsefromfastapi.staticfilesimportStaticFilesapp=FastAPI()app.mount("/imgs",StaticFiles(directory="imgs"),name='images')@app.get("/",response_class=HTMLResponse)defserve():return""" <html> <head> <title></title> </head> <body> <img src="imgs/g.png"> <h1>Hello World</h1> </body> </html>"""
To serve static images or files, we utilised FastAPI'sStaticFiles
class. This class is derived directly from Starlette; we can simply import that class fromfastapi.staticfiles
instead ofstarlette.staticfiles
for convenience.
We stored our static image inside theimgs
directory present in the root directory and we passed this directory to theStaticFiles()
instance andmounted it in the/imgs
path.
Mounting here means setting up or adding a completely independent application in a specific path, which will then take care of handling all the sub-paths.
Here,
/img
- is a sub-path on which the sub-application will be mounted.
directory='img'
- refers to the static directory where our static images or files will be stored.
name='images'
- this name will be used by FastAPI internally or we can skip this also.
The path operation decorator@app.get("/", response class=HTMLResponse)
was then created, and you'll notice that we passedHTMLResponse
, which will assist in returning the HTML response directly from FastAPI.
Theresponse_class
will also be used to define themedia type
of the response and in this case, in the HTTP header,Content-Type
will be set totext/html
.
Then we created apath operation function calledserve()
and returned the HTML in which we passed our static image path("imgs/g.png"
) in thesrc
attribute of the<img>
tag.
Now run the server using the uvicorn.
uvicornstatic_img:app--reload
Here's the API response
If we see the API response in the Postman, then we'll see a raw HTML as a response by the FastAPI.
Due toHTMLResponse
in the path operation decorator, the browser parses our HTML like it usually does for any HTML file.
Another approach
This approach might be beneficial if you are working on a serious project and want your code to be more readable and manageable.
In this approach, we'll be usingjinja and instead of usingHTMLResponse
we'll be usingJinja2Templates
to render the HTML response.
In order to use the
Jinja2Templates
, we need to install thejinja2
library and it can be installed using the pip by running the commandpip install jinja2
.
# files.pyfromfastapiimportFastAPI,Request# The modules are directly coming from starlettefromfastapi.staticfilesimportStaticFilesfromfastapi.templatingimportJinja2Templatesapp=FastAPI()app.mount("/static",StaticFiles(directory="static"),name="static")templates=Jinja2Templates(directory="templates")@app.get("/")defstatic(request:Request):returntemplates.TemplateResponse("index.html",{"request":request})
The process is pretty much similar to the first approach but in this approach, we imported some more classes such asRequest
andJinja2Templates
from thefastapi
andfastapi.templating
modules, respectively.
Like in the first approach, we mounted theStaticFiles(directory="static")
instance in a path/static
.
Then using theJinja2Templates(directory="templates")
, we specified the directory calledtemplates
from where the FastAPI will look for the.html
files and stored them inside thetemplates
variable.
Then we created the path operation decorator@app.get("/")
and followed by it created the path operation function calledstatic
and passed theRequest
class to the parameterrequest
.
Then we returned theTemplateResponse("index.html", {"request": request})
. In this case, we passed therequest
as part of the key-value pairs in theJinja2 context. This will allow us to inject the dynamic content that we desire when the template is rendered.
Now write HTML code in theindex.html
file inside thetemplates directory.
<html><head><linkhref="{{ url_for('static') }}"/><title>Serving Image Files Using FastAPI</title></head><body><imgsrc="{{ url_for('static', path='assets/GP.png') }}"/></body></html>
Just add the code<link href="{{ url_for('static') }}" />
to link the directory namedstatic as shown in the above code.
Then in thesrc
attribute of the<img>
tag, we specified the path to the static image using jinja like this{{ url_for('static', path='assets/GP.png') }}
.
Now run the server using the commanduvicorn files:app --reload
.
Here's the response
If we see what we get when we send the request to the API using Postman.
We can include the same HTML as above in ourindex.html
and get the same result but it won't be a good practice at all.
Serving user-provided images
In this approach, we'll have a form where the user can enter an image that will be displayed on the frontend.
When a user uploads an image file, it is sent asform data, and we must install the following library to receive it.
pipinstallpython-multipart
The following code will be responsible for uploading and displaying the user-provided image.
# dynamic.pyfromfastapiimportFastAPI,UploadFile,File,Requestfromfastapi.templatingimportJinja2Templatesimportbase64app=FastAPI()templates=Jinja2Templates(directory="templates")@app.get("/")defdynamic_file(request:Request):returntemplates.TemplateResponse("dynamic.html",{"request":request})@app.post("/dynamic")defdynamic(request:Request,file:UploadFile=File()):data=file.file.read()file.file.close()# encoding the imageencoded_image=base64.b64encode(data).decode("utf-8")returntemplates.TemplateResponse("dynamic.html",{"request":request,"img":encoded_image})
The classUploadFile
and functionFile
from fastapi were imported into the code above to assist in handling the reading and uploading of the image file. We imported thebase64
library to handle image encoding and decoding.
We created a path operation decorator (@app.get("/")
) and right below it created a path operation function calleddynamic_files
that will render thedynamic.html
file on the path"/"
.
Then we created a path operation decorator(@app.post("/dynamic")
) to handle thePost request and then we created a path operation function calleddynamic
and passedrequest: Request
andfile: UploadFile = File()
. Then we read the image file and stored it inside thedata
variable and finally closed the file.
Here,file.file.read()
is equivalent toUploadFile.file.read()
.UploadFile
has afile
attribute which is a file-like object andread()
is a method also provided byUploadFile
to read the bytes/characters of the image.
If we would have defined theasynchronous path operation function, then we could read the file using
await file.read()
.
Now we've read the bytes of the image file and it needs to be encoded in a string that can be returned and passed to the template. If we look at the image's bytes, it would look like the following.
xbd\x02\xacf\xb6\xaa\x02\x889\x95\xac6Q\x80\xa4<1\xcd\xef\xf7R\xc2\xb2<j\x08&6\xa8.s\x16M!i\xa8#\xe7RM$\x15\x00\x84\x81...x00\x00P\x1d\x01\x04\x00\x00\x00\x00\x00\xa8\x8e\x00\x02\x00\x00\x00\x00\x00T\xe7\xff\x03a\xbc\xbee\x93\xf6V\xfc\x00\x00\x00\x00IEND\xaeB`\x82'
encoded_image = base64.b64encode(data).decode("utf-8")
will encode the bytes of the image file stored inside thedata
variable using theutf-8
encoding into a string and the encoded string will be stored inside theencoded_image
variable. Now, if we examine the encoded string, they will all appear to be random characters and resemble the following.
+jYs7u5Zhy29PmXSh8aQtPim5Y4rC0OKzTQj5RYpzj2IBBCw3a7A0nEMRI1IbLj+uYSjUq/60lOuN3uaNuWvu85WK/RlHj67JyuW/H04oL16hCdtjvx6PFTD...I4AAAAAAAADVEUAAAAAAAIDqCCAAAAAAAEB1BBAAAAAAAKA6AggAAAAAAFAdAQQAAAAAAKiOAAIAAAAAAFTn/wNhvL5lk/ZW/AAAAABJRU5ErkJggg==
Then we returned thedynamic.html
file and passed the variableencoded_image
as a value of the key"img"
.
Writing template
dynamic.html
file
<html><head><title>Rendering Dynamic Images Using FastAPI</title></head><body><formaction="/dynamic"enctype="multipart/form-data"method="POST"><inputname="file"type="file"/><inputtype="submit"/></form> {% if img %}<h1>Rendered Image</h1><imgsrc="data:image/jpeg;base64,{{ img }}"/> {% else %}<h1>Image will be render here...</h1> {% endif %}</body></html>
In ourdynamic.html
file, we added a form tag that handles aPOST request from the"/dynamic"
URL, and we used theenctype
attribute with the value"multipart/form-data"
to handle file uploading through the form. Then, within the form, we added twoinput tags: one for selecting the image and one for submitting it.
Then we used jinja syntax to create anif-else condition and inserted animg tag with asrc
attribute containing our image. We passed"data:image/jpeg;base64, img"
because we need to use this format to display base64 images in HTML.
Testing API
Go to the URL127.0.0.1:8000
.
We chose the image that will be displayed. The image will be displayed if we click the submit button.
What if we want to save the user-provided image in a particular folder?
Saving user-provided images
To save user-provided images in a specific folder, assume we have a folder calleduploads and want to save the images there.
# dynamic.pyfromfastapiimportFastAPI,UploadFile,File,Requestfromfastapi.templatingimportJinja2Templatesimportbase64app=FastAPI()templates=Jinja2Templates(directory="templates")@app.get("/")defdynamic_file(request:Request):returntemplates.TemplateResponse("dynamic.html",{"request":request})@app.post("/dynamic")defdynamic(request:Request,file:UploadFile=File()):data=file.file.read()# Image will be saved in the uploads folder prefixed with uploaded_withopen("uploads/saved_"+file.filename,"wb")asf:f.write(data)file.file.close()# encoding and decoding the image bytesencoded_image=base64.b64encode(data).decode("utf-8")returntemplates.TemplateResponse("dynamic.html",{"request":request,"img":encoded_image})
We used theopen()
function and passed the path to ouruploads
folder with the name we want to prefix with the name of the image and opened it inwrite
mode and then usedf.write(data)
to create an image within the uploads folder with the namesaved_xyz.png.
Testing
Run the server usinguvicorn dynamic:app --reload
and go to the URL127.0.0.1:8000
.
Conclusion
If you have worked with the Flask web framework, you may find it similar. FastAPI is a modern, high-performance web framework for building APIs using Python.
In this article, we've learned to display the static and user-provided(dynamic) images on the frontend using certain classes and modules from FastAPI. We saw two approaches for displaying static files on the frontend using FastAPI and then saw the process for displaying user-provided images and then the process for saving them in a particular folder.
Through this, we came across responses in FastAPI, requesting files, reading and uploading files, jinja templates and handling static files.
🏆Other articles you might be interested in if you liked this one
✅Display static and dynamic images on the frontend using Flask.
✅Get started with FastAPI - A beginner guide.
✅Build your first command line interface using Python.
✅Learn how to execute the dynamically generated code using Python.
✅Public, Protected and Private access modifiers in Python.
✅Perform high-level file operation using shutil in Python.
✅Extract information from the web pages using Python and BeautifulSoup.
That's all for now
Keep Coding✌✌
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse