FastAPI 静态文件服务详解

举报
时光不写 发表于 2026/07/22 14:20:37 2026/07/22
【摘要】 FastAPI 中如何挂载和提供静态文件服务,包括 StaticFiles 配置、目录挂载与路径查找规则。

FastAPI 不仅能构建 API 接口,还内置了对静态文件服务的支持。通过 StaticFiles,你可以轻松地将图片、CSS、JavaScript 或任意静态资源目录挂载到应用中,像传统 Web 服务器一样对外提供文件访问。

为什么需要静态文件服务

大多数 Web 应用除了 API 接口外,还需要直接向浏览器提供静态资源。比如:

  • 前端打包后的 HTML、CSS、JS 文件
  • 用户上传的头像、附件
  • 接口文档引用的图片资源
  • favicon 等站点图标

FastAPI 基于 Starlette,而 Starlette 提供了 StaticFiles 这个 ASGI 应用,可以直接挂载到 FastAPI 的路由体系中使用。

基础用法:挂载目录

StaticFiles 位于 starlette.staticfiles(也可从 fastapi.staticfiles 导入),使用 app.mount() 将其挂载到指定路径前缀下:

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

# 将本地的 static 目录挂载到 /static 路径
app.mount("/static", StaticFiles(directory="static"), name="static")

目录结构假设如下:

project/
├── main.py
└── static/
    ├── logo.png
    ├── style.css
    └── images/
        └── banner.jpg

挂载后访问规则:

请求路径 对应本地文件
/static/logo.png static/logo.png
/static/style.css static/style.css
/static/images/banner.jpg static/images/banner.jpg

注意:app.mount() 挂在指定路径下的 子应用,该路径本身不会被 FastAPI 的路由处理 — 完全由 StaticFiles 接管。

关键参数详解

StaticFiles 构造函数的常用参数:

StaticFiles(
    directory="static",      # 必填:本地文件目录
    packages=None,            # 可选:Python 包列表(如 Jinja2 静态资源)
    html=False,               # 是否将 directory 下的 HTML 文件当作文本返回
    check_dir=True,           # 启动时检查目录是否存在
    follow_symlinks=False,    # 是否跟随符号链接
)

html 参数

html=True 时,访问 index.html 等文件会以 text/html Content-Type 返回,适合挂载前端 SPA 的打包产物:

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

packages 参数

用于引用已安装 Python 包内的静态资源:

app.mount("/static", StaticFiles(packages=[("my_package", "static")]), name="static")

这会查找 my_package 包内的 static 子目录。

挂载顺序与路由优先级

挂载路径优先于路由。Starlette 按注册顺序匹配,一旦某个 mount 路径前缀命中,后续路由就不会再检查。错误示例:

app = FastAPI()

# ❌ 错误:先挂载 /,会吞掉所有 API 路由
app.mount("/", StaticFiles(directory="dist", html=True), name="frontend")

@app.get("/api/hello")  # 永远不会被命中!
async def hello():
    return {"message": "Hello"}

正确做法是 API 路由注册在前,静态文件挂载在后(或者用不同路径前缀):

app = FastAPI()

# ✅ API 路由在前
@app.get("/api/hello")
async def hello():
    return {"message": "Hello"}

# ✅ 静态文件挂载在后,且路径前缀不冲突
app.mount("/", StaticFiles(directory="dist", html=True), name="frontend")

或者更清晰的方式 — 使用不同前缀:

app = FastAPI()

@app.get("/api/hello")
async def hello():
    return {"message": "Hello"}

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

运行时动态添加静态目录

如果需要根据运行时条件挂载不同目录(如按用户区分的上传目录),可以在路由层手动构造 StaticFiles 应用后挂载:

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
import os

app = FastAPI()

@app.get("/api/hello")
async def hello():
    return {"message": "Hello"}


# 假如通过环境变量控制静态文件目录
static_dir = os.getenv("STATIC_DIR", "static")
app.mount("/static", StaticFiles(directory=static_dir), name="static")

如果需要更复杂的逻辑(如按用户 id 分流),则不适合用 mount,应改用 FileResponse 在路由中手动处理文件返回。

配合 FileResponse 精确控制

对于需要权限校验或动态路径的单个文件返回,使用 FileResponse 而非全局挂载:

from fastapi import FastAPI
from fastapi.responses import FileResponse
import os

app = FastAPI()

@app.get("/download/{filename}")
async def download_file(filename: str):
    file_path = os.path.join("uploads", filename)
    if not os.path.isfile(file_path):
        return {"error": "文件不存在"}
    return FileResponse(
        path=file_path,
        filename=filename,
        media_type="application/octet-stream"
    )

FileResponse 支持的关键参数:

参数 说明
path 文件路径
filename 下载时显示的文件名(触发 Content-Disposition)
media_type 显式指定 MIME 类型
headers 自定义响应头字典

挂载多个静态目录

FastAPI 支持同时挂载多个静态目录到不同路径:

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

@app.get("/api/health")
async def health():
    return {"status": "ok"}

# 前端资源
app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="frontend")

# 用户上传文件
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")

# 静态资源
app.mount("/static", StaticFiles(directory="static"), name="static")

各挂载点互不干扰,各自管理自己的目录。

生产环境注意事项

StaticFiles 使用 Python 的 aiofiles 做异步文件 I/O,在开发和小规模场景足够。但在高并发生产环境中,建议:

  1. 由 Nginx / Caddy 等反向代理直接处理静态文件请求
  2. FastAPI 只处理 API 逻辑,静态文件路径在反向代理层拦截

Nginx 配置示例片段:

server {
    listen 80;
    server_name example.com;

    # 静态文件由 Nginx 直接返回
    location /static/ {
        alias /app/static/;
        expires 30d;
    }

    # API 请求转发给 FastAPI
    location /api/ {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
    }
}

总结

场景 推荐方案
全局静态资源目录 app.mount("/static", StaticFiles(...))
前端 SPA 部署 app.mount("/", StaticFiles(..., html=True))
需要权限控制的文件下载 路由中使用 FileResponse
高并发生产环境 Nginx 直接处理,FastAPI 只做 API

掌握 StaticFilesFileResponse 两种方式,就能灵活应对从开发到生产的不同静态文件需求。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。