Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serve static files in FastAPI

I am trying to serve static files that I have in a package_docs directory. When I open in the browzer:

http://127.0.0.1:8001/packages/docs/index.html , the page is running.

But I want to open the page: http://127.0.0.1:8001/packages/docs/

without the source file. And the output is 404 Not Found

app.mount("/packages/docs", 
    StaticFiles(directory=pkg_resources.resource_filename(__name__, 'package_docs')
    ), 
    name="package_docs")

@app.get("/packages/docs/.*", include_in_schema=False)
def root():
    return HTMLResponse(pkg_resources.resource_string(__name__, "package_docs/index.html"))


app.include_router(static.router)
app.include_router(jamcam.router, prefix="/api/v1/cams", tags=["jamcam"])

How can I change my code? Any advice will be helpful. Thank you in advance.

like image 823
Christy Nakou Avatar asked Jun 18 '20 17:06

Christy Nakou


People also ask

Can FastAPI serve static files?

FastAPI provides the same starlette. staticfiles as fastapi. staticfiles just as a convenience for you, the developer.

Can Django serve static files in production?

During development, as long as you have DEBUG set to TRUE and you're using the staticfiles app, you can serve up static files using Django's development server. You don't even need to run the collecstatic command.

What is a static file example?

Any kind of file can be served as static content as long as it does not change in response to a user's actions or inputs. This includes images, JavaScript files, CSS files, videos, Flash files, even web pages.

What is the difference between'static'and'static directory'in fastapi?

The directory="static" refers to the name of the directory that contains your static files. The name="static" gives it a name that can be used internally by FastAPI. All these parameters can be different than " static ", adjust them with the needs and specific details of your own application.

What is the default path for static files in fastapi?

So, any path that starts with "/static" will be handled by it. The directory="static" refers to the name of the directory that contains your static files. The name="static" gives it a name that can be used internally by FastAPI.

How does fastapi encode data from forms?

Data from forms is normally encoded using the "media type" application/x-www-form-urlencoded when it doesn't include files. But when the form includes files, it is encoded as multipart/form-data. If you use File, FastAPI will know it has to get the files from the correct part of the body.

Can I use multiple subfolders in Python fastapi?

Note: it’s fine to use even more subfolders if you want, as long as these folders all go inside the static folder. Here, we want to tell Python FastAPI that we want to allow users to access these static files. We only need to add 2 lines of code to make this work.


4 Answers

There's a html option in Starlette that can be used within FastAPI. Starlette Documentation

This would let you have something such as:

app.mount("/site", StaticFiles(directory="site", html = True), name="site")

Which would parse /site to /site/index.html, /site/foo/ to /site/foo/index.html, etc.

The other answers can help you redirect if you want to change the folder names in a way not handled by using "directory = /foo", but this is the simplest option if you simply want to load the associated .html files.

like image 117
Justin Malloy Avatar answered Oct 24 '22 03:10

Justin Malloy


You need to use FastAPI's TemplateResponse (actually Starlette's):

from fastapi import Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

app.mount("/static", StaticFiles(directory="static"), name="static")

templates = Jinja2Templates(directory="package_docs")

@app.get("/items/{id}")
async def example(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Request as part of the key-value pairs in the context for Jinja2. So, you also have to declare it as query parameter. and you have to specify a html file you want to render it with Jinja ("your.html", {"request": request})

Also to return a HTMLResponse directly you can use HTMLResponse from fastapi.responses

from fastapi.responses import HTMLResponse

@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return """
    <html>
        <head>
            <title></title>
        </head>
        <body>
        </body>
    </html>
    """

You can read more about custom response from FastAPI Custom Responses

like image 44
Yagiz Degirmenci Avatar answered Oct 24 '22 03:10

Yagiz Degirmenci


From the docs

The first "/static" refers to the sub-path this "sub-application" will be "mounted" on. So, any path that starts with "/static" will be handled by it.

This means that you mount your directory at http://127.0.0.1:8001/packages/docs/ , but you then need to either specify a file in the URL, or add a handler as you did. The problem though, is that since you mounted the path first, it will not take into consideration the following paths that include part of the path.

A possibility is to first specify the path for http://127.0.0.1:8001/packages/docs/ so that it is handled by fastapi and then to mount the folder, serving static files.

Also, I would redirect users asking for http://127.0.0.1:8001/packages/docs/ to http://127.0.0.1:8001/packages/docs/index.html

like image 26
lsabi Avatar answered Oct 24 '22 03:10

lsabi


If app.mount is not an option, you can always manually read the file and respond with its content...

For example, if your static files are inside /site then:

from os.path import isfile
from fastapi import Response
from mimetypes import guess_type


@app.get("/site/{filename}")
async def get_site(filename):
    filename = './site/' + filename

    if not isfile(filename):
        return Response(status_code=404)

    with open(filename) as f:
        content = f.read()

    content_type, _ = guess_type(filename)
    return Response(content, media_type=content_type)


@app.get("/site/")
async def get_site_default_filename():
    return await get_site('index.html')
like image 1
Nitsan BenHanoch Avatar answered Oct 24 '22 03:10

Nitsan BenHanoch