当前位置: 首页 > news >正文

FastAPI 静态文件

FastAPI 静态文件学习笔记

一、基本用法 —StaticFiles

1. 挂载静态文件目录

fromfastapiimportFastAPIfromfastapi.staticfilesimportStaticFiles app=FastAPI()# 将 ./static 目录挂载到 /static 路径app.mount("/static",StaticFiles(directory="static"),name="static")

目录结构

project/ ├── main.py └── static/ ├── css/ │ └── style.css ├── js/ │ └── app.js └── images/ └── logo.png

访问方式

http://localhost:8000/static/css/style.css http://localhost:8000/static/js/app.js http://localhost:8000/static/images/logo.png

2. 参数说明

参数类型默认值说明
directorystr/Path必填静态文件目录路径
htmlboolFalse是否启用 HTML 模式
check_dirboolTrue启动时检查目录是否存在

二、HTML 模式

1. 启用 HTML 模式

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

HTML 模式行为

请求路径文件存在情况响应
/index.html存在返回index.html
/aboutabout.html存在返回about.html
/about/about/index.html存在返回about/index.html
/style.cssstyle.css存在返回style.css
/missing无匹配文件404

2. 典型用途 — 托管 SPA 前端

# Vue / React 构建产物app.mount("/",StaticFiles(directory="dist",html=True),name="spa")
dist/ ├── index.html ├── assets/ │ ├── index.abc123.js │ └── index.def456.css └── favicon.ico

注意:SPA 的前端路由(如/about/user/1)需要服务端统一返回index.htmlhtml=True只处理.html文件匹配,不支持 SPA fallback。完整 SPA 托管方案见第五节。


三、挂载位置与路由优先级

1.app.mount的特性

mount创建的是一个子应用,会匹配该路径下的所有请求,不再经过主应用的路由匹配:

app=FastAPI()@app.get("/static/data")# ← 永远不会被访问到asyncdefget_data():return{"data":"hello"}app.mount("/static",StaticFiles(directory="static"),name="static")# /static/* 的所有请求都被 StaticFiles 拦截

2. 正确的挂载顺序

app=FastAPI()# ① 先注册 API 路由@app.get("/api/data")asyncdefget_data():return{"data":"hello"}# ② 最后挂载静态文件(避免覆盖 API 路由)app.mount("/static",StaticFiles(directory="static"),name="static")

3. 挂载多个静态目录

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

四、在 Jinja2 模板中引用静态文件

fromfastapiimportFastAPI,Requestfromfastapi.staticfilesimportStaticFilesfromfastapi.templatingimportJinja2Templates app=FastAPI()app.mount("/static",StaticFiles(directory="static"),name="static")templates=Jinja2Templates(directory="templates")@app.get("/page")asyncdefpage(request:Request):returntemplates.TemplateResponse("page.html",{"request":request})

模板中引用

<!-- 使用 url_for 动态生成路径 --><linkrel="stylesheet"href="{{ url_for('static', path='/css/style.css') }}"><scriptsrc="{{ url_for('static', path='/js/app.js') }}"></script><imgsrc="{{ url_for('static', path='/images/logo.png') }}"alt="logo">

url_for的第一个参数是mount时的name,第二个参数path是文件在静态目录中的相对路径。


五、SPA 前端托管完整方案

Vue / React 等单页应用需要所有未匹配路径返回index.html

方案一:中间件 fallback

fromfastapiimportFastAPI,Requestfromfastapi.staticfilesimportStaticFilesfromfastapi.responsesimportFileResponse app=FastAPI()# API 路由@app.get("/api/data")asyncdefget_data():return{"data":"hello"}# 静态资源(js/css/images)app.mount("/assets",StaticFiles(directory="dist/assets"),name="assets")# SPA fallback:未匹配的路由返回 index.html@app.get("/{full_path:path}")asyncdefserve_spa(request:Request,full_path:str):returnFileResponse("dist/index.html")

方案二:自定义静态文件中间件

importosfromfastapiimportFastAPI,Requestfromfastapi.responsesimportFileResponse,Responsefromstarlette.middleware.baseimportBaseHTTPMiddleware app=FastAPI()DIST_DIR="dist"classSPAMiddleware(BaseHTTPMiddleware):asyncdefdispatch(self,request:Request,call_next):response=awaitcall_next(request)# 如果路由返回 404 且是 HTML 请求,返回 index.htmlifresponse.status_code==404:accept=request.headers.get("accept","")if"text/html"inaccept:index_path=os.path.join(DIST_DIR,"index.html")ifos.path.exists(index_path):returnFileResponse(index_path)returnresponse app.add_middleware(SPAMiddleware)app.mount("/assets",StaticFiles(directory=f"{DIST_DIR}/assets"),name="assets")

方案三:纯静态挂载(最简单)

app=FastAPI()# API 路由@app.get("/api/data")asyncdefget_data():return{"data":"hello"}# 静态文件(html=True 自动处理 index.html)app.mount("/",StaticFiles(directory="dist",html=True),name="spa")

局限html=True只在请求路径对应.html文件存在时返回,不支持 SPA 的动态路由(如/user/123)。


六、文件上传与静态文件服务结合

importosimportuuidfromfastapiimportFastAPI,UploadFile,Filefromfastapi.staticfilesimportStaticFiles app=FastAPI()UPLOAD_DIR="uploads"os.makedirs(UPLOAD_DIR,exist_ok=True)@app.post("/upload")asyncdefupload_file(file:UploadFile=File(...)):# 生成唯一文件名ext=os.path.splitext(file.filename)[1]filename=f"{uuid.uuid4().hex}{ext}"filepath=os.path.join(UPLOAD_DIR,filename)# 保存文件withopen(filepath,"wb")asf:content=awaitfile.read()f.write(content)return{"filename":filename,"url":f"/uploads/{filename}",}# 挂载上传目录app.mount("/uploads",StaticFiles(directory=UPLOAD_DIR),name="uploads")

七、生产环境注意事项

1. Nginx 反向代理直接服务静态文件

生产环境建议由 Nginx 直接处理静态文件,性能更好:

server { listen 80; server_name example.com; # Nginx 直接服务静态文件 location /static/ { alias /var/www/static/; expires 30d; add_header Cache-Control "public, immutable"; } # API 请求转发给 FastAPI location /api/ { proxy_pass http://127.0.0.1:8000; } }

2. 缓存控制

fromstarlette.responsesimportResponsefromfastapi.staticfilesimportStaticFilesclassCachedStaticFiles(StaticFiles):asyncdeflookup_path(self,path:str):full_path,stat_result=awaitsuper().lookup_path(path)ifstat_result:# 根据文件扩展名设置缓存ifpath.endswith((".js",".css",".woff2",".png",".jpg")):self.headers["Cache-Control"]="public, max-age=31536000, immutable"else:self.headers["Cache-Control"]="public, max-age=3600"returnfull_path,stat_result app.mount("/static",CachedStaticFiles(directory="static"),name="static")

3. 目录安全

风险防护措施
目录遍历攻击StaticFiles默认禁止..路径
敏感文件泄露不要将配置文件、.env放在静态目录
上传恶意文件限制文件类型、重命名文件、隔离上传目录
大文件消耗带宽Nginx 层限制请求体大小

八、StaticFilesvs 其他方案

方案适用场景性能灵活性
StaticFiles开发环境、小型项目
Nginx 直接服务生产环境
CDN全球分发、高流量最高最高
云存储(OSS/S3)用户上传文件、海量存储

推荐

开发环境 → StaticFiles(零配置) 生产环境 → Nginx 直接服务静态文件 + CDN 加速 用户上传 → 云存储(OSS / S3)+ CDN

九、完整示例

fromfastapiimportFastAPI,Request,UploadFile,Filefromfastapi.staticfilesimportStaticFilesfromfastapi.responsesimportFileResponseimportos,uuid app=FastAPI()# ---- 目录准备 ----os.makedirs("static/css",exist_ok=True)os.makedirs("static/js",exist_ok=True)os.makedirs("uploads",exist_ok=True)# ---- API 路由 ----@app.get("/api/hello")asyncdefhello():return{"message":"Hello, FastAPI!"}@app.post("/api/upload")asyncdefupload(file:UploadFile=File(...)):ext=os.path.splitext(file.filename)[1]filename=f"{uuid.uuid4().hex}{ext}"withopen(f"uploads/{filename}","wb")asf:f.write(awaitfile.read())return{"url":f"/uploads/{filename}"}# ---- 静态文件挂载 ----app.mount("/static",StaticFiles(directory="static"),name="static")app.mount("/uploads",StaticFiles(directory="uploads"),name="uploads")# ---- SPA fallback(放最后)----@app.get("/{full_path:path}")asyncdefspa_fallback(request:Request,full_path:str):returnFileResponse("static/index.html")

十、注意事项

  1. mount必须放在路由注册之后:否则会拦截所有匹配路径的请求,导致 API 路由不可达。
  2. 目录必须存在:默认check_dir=True,目录不存在会抛异常。启动前确保目录已创建。
  3. name参数的作用:用于url_for反向生成 URL,建议始终设置。
  4. 开发 vs 生产StaticFiles适合开发,生产环境推荐 Nginx / CDN。
  5. 路径以/结尾:访问目录路径时,StaticFiles会自动查找index.html(需html=True)。
http://www.jsqmd.com/news/773005/

相关文章:

  • 【2026实战】双栈协同:Python+Go混合架构完整实战
  • 解密TlbbGmTool:如何高效管理天龙八部单机版游戏数据的3个核心问题
  • XSLT 实例
  • VS3000芯片深度体验:除了传4K,它的USB和网络功能在视频会议里到底有多香?
  • 高频脉冲电源选购:高性价比靠谱产品筛选策略解析
  • Java 代码质量度量指标:评估代码质量的标准
  • FastAPI 安全认证
  • ComfyUI Manager:AI绘画插件的智能管家,5分钟打造高效创作环境
  • Fast-GitHub加速插件:3步解决国内GitHub访问难题的终极方案
  • 全面解决Kohya_ss安装问题的10个专业技巧:从环境配置到高效训练
  • runprompt:基于Dotprompt格式的命令行LLM提示词工程化与自动化工具
  • Botty终极指南:5步配置暗黑2重制版24小时自动化MF脚本
  • 读源码像读小说?试了 DeepWiki 和 Zread,我再也不想裸读 GitHub 了
  • Moodle自动化工具:零配置API客户端与AI助手集成实战
  • 终极ComfyUI-Manager完全指南:快速部署与高效管理自定义节点
  • Java后端面试:核心基础考点,String、StringBuilder、StringBuffer 区别详解
  • 别再死记硬背了!用Verilog手把手带你理解CRC校验的电路核心(附串行/并行实现代码)
  • 节后系统恢复中的技术操作:批量处理、数据一致性与人机协作
  • 做了一个 App Store 全球最低价查询工具:支持 App、订阅和内购价格对比
  • 打卡信奥刷题(3225)用C++实现信奥题 P8370 [POI 2001 R3] Goldmine
  • 2026年郑州装修公司推荐排名前十强,口碑好性价比高的靠谱公司盘点 - 速递信息
  • 本土化赋能:Gitee如何重塑中国开发者的代码托管体验
  • Mermaid Live Editor:如何用代码思维解决图表绘制的三大痛点?
  • BDInfo深度解析:蓝光光盘技术规格分析的完整解决方案
  • AISMM×ISO 27001×NIST RMF三模融合实践:一位CISO亲授的72小时风险响应加速方案
  • DayZ社区离线模式终极指南:打造专属末日生存实验室
  • 终极指南:如何用Python快速获取专业级金融数据
  • ChatGPT Atlas全解析:OpenAI原生AI浏览器核心能力+macOS离线安装完整指南
  • 3分钟手机端刷入Android内核:Horizon Kernel Flasher终极指南
  • Python数据分析如何填充缺失日期_Pandas的asfreq技巧