FastAPI后端工程化項目記錄

以下是一個使用fastapi上傳視頻的接口,記錄一下工程化后端程序的業務邏輯

重點是代碼如何抽離

項目結構優化

project/
├── .env                      # 環境變量配置
├── app/
│   ├── __init__.py
│   ├── main.py               # 主應用入口
│   ├── core/                 # 核心配置
│   │   ├── __init__.py
│   │   ├── config.py         # 應用配置
│   │   ├── database.py       # 數據庫配置
│   │   └── middleware.py     # 中間件配置
│   ├── models/               # 數據模型
│   │   ├── __init__.py
│   │   └── video.py          # 視頻模型
│   ├── schemas/              # Pydantic模型
│   │   ├── __init__.py
│   │   └── video.py          # 視頻響應模型
│   ├── services/             # 業務邏輯
│   │   ├── __init__.py
│   │   └── video_service.py  # 視頻服務
│   ├── utils/                # 工具函數
│   │   ├── __init__.py
│   │   └── file_utils.py     # 文件處理工具
│   └── routers/              # 路由模塊
│       ├── __init__.py
│       ├── video.py          # 視頻路由
│       └── train.py          # 訓練路由
├── static/                   # 靜態文件
│   └── videos/               # 視頻存儲
└── alembic/                  # 數據庫遷移

1. 數據庫配置 (app/core/database.py)

from tortoise import Tortoise
from tortoise.contrib.fastapi import register_tortoise
from app.core.config import settingsasync def init_db():"""初始化數據庫連接"""await Tortoise.init(db_url=settings.DATABASE_URL,modules={'models': ['app.models']},generate_schemas=False  # 禁用自動建表(適配已有表))def setup_database(app):"""注冊數據庫到FastAPI應用"""register_tortoise(app,db_url=settings.DATABASE_URL,modules={'models': ['app.models']},generate_schemas=False,  # 禁用自動建表add_exception_handlers=True)

2. 應用配置 (app/core/config.py)

import os
from pydantic import BaseSettingsclass Settings(BaseSettings):DATABASE_URL: str = "mysql://user:password@localhost:3306/video_service"UPLOAD_DIR: str = "static/videos"ALLOWED_EXTENSIONS: set = {".mp4", ".mov", ".avi", ".mkv", ".webm"}MAX_FILE_SIZE_MB: int = 2000MAX_FILE_SIZE: int = MAX_FILE_SIZE_MB * 1024 * 1024class Config:env_file = ".env"settings = Settings()

3. 數據模型 (app/models/video.py)

from tortoise.models import Model
from tortoise import fieldsclass VideoRecord(Model):id = fields.IntField(pk=True)original_filename = fields.CharField(max_length=255)saved_filename = fields.CharField(max_length=255)server_path = fields.CharField(max_length=512)file_size = fields.BigIntField()file_type = fields.CharField(max_length=10)upload_time = fields.DatetimeField(auto_now_add=True)unique_id = fields.UUIDField()class Meta:table = "video_records"  # 指定已有表名table_description = None  # 禁用自動字段修改

4. Pydantic模型 (app/schemas/video.py)

from datetime import datetime
from pydantic import BaseModelclass VideoUploadResponse(BaseModel):message: strsaved_filename: strpath: strabsolute_path: stroriginal_filename: strsize: intrecord_id: intupload_time: datetimefile_type: str

5. 文件工具 (app/utils/file_utils.py)

import os
import uuid
from datetime import datetime
from app.core.config import settingsdef ensure_upload_dir():"""確保上傳目錄存在"""os.makedirs(settings.UPLOAD_DIR, exist_ok=True)def is_valid_video(filename: str) -> bool:"""檢查文件擴展名是否為允許的視頻格式"""return any(filename.lower().endswith(ext) for ext in settings.ALLOWED_EXTENSIONS)def generate_new_filename(original_filename: str) -> str:"""生成唯一的帶時間戳的新文件名"""ext = os.path.splitext(original_filename)[1]timestamp = datetime.now().strftime("%Y%m%d%H%M%S")unique_id = uuid.uuid4().hex[:6]return f"video_{timestamp}_{unique_id}{ext}"

6. 視頻服務 (app/services/video_service.py)

import os
import logging
from fastapi import HTTPException
from app.models.video import VideoRecord
from app.utils.file_utils import generate_new_filename, is_valid_video
from app.core.config import settingslogger = logging.getLogger(__name__)async def upload_video(file):"""視頻上傳服務邏輯"""# 驗證文件類型if not is_valid_video(file.filename):raise HTTPException(400, f"不支持的文件格式。僅支持: {', '.join(settings.ALLOWED_EXTENSIONS)}")# 計算文件大小file_size = 0while chunk := await file.read(10 * 1024 * 1024):  # 10MB chunksfile_size += len(chunk)await file.seek(0)# 檢查文件大小if file_size > settings.MAX_FILE_SIZE:raise HTTPException(413, f"文件太大,最大允許 {settings.MAX_FILE_SIZE_MB}MB")# 生成新文件名new_filename = generate_new_filename(file.filename)save_path = os.path.join(settings.UPLOAD_DIR, new_filename)abs_save_path = os.path.abspath(save_path)file_ext = os.path.splitext(file.filename)[1].lower()# 保存文件try:with open(save_path, "wb") as buffer:while chunk := await file.read(10 * 1024 * 1024):buffer.write(chunk)logger.info(f"視頻保存成功: {abs_save_path}")except Exception as e:logger.error(f"文件保存失敗: {abs_save_path}, 錯誤: {str(e)}")if os.path.exists(save_path):os.remove(save_path)raise HTTPException(500, f"文件保存失敗: {str(e)}")finally:await file.close()# 創建數據庫記錄try:video_record = await VideoRecord.create(original_filename=file.filename,saved_filename=new_filename,server_path=abs_save_path,file_size=file_size,file_type=file_ext)logger.info(f"數據庫記錄創建成功,ID: {video_record.id}")except Exception as e:if os.path.exists(save_path):os.remove(save_path)logger.error(f"數據庫操作失敗: {str(e)}")raise HTTPException(500, f"數據庫操作失敗: {str(e)}")return {"saved_filename": new_filename,"path": f"/{settings.UPLOAD_DIR}/{new_filename}","absolute_path": abs_save_path,"original_filename": file.filename,"size": file_size,"record_id": video_record.id,"upload_time": video_record.upload_time,"file_type": file_ext}

7. 視頻路由 (app/routers/video.py)

from fastapi import APIRouter, UploadFile, File
from fastapi.responses import JSONResponse
from app.services.video_service import upload_video
from app.schemas.video import VideoUploadResponserouter = APIRouter()@router.post("/upload-video", response_model=VideoUploadResponse)
async def api_upload_video(file: UploadFile = File(...)):"""視頻上傳接口"""result = await upload_video(file)return JSONResponse(status_code=200,content={"message": "視頻上傳并記錄成功",**result})

8. 主應用 (app/main.py)

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from contextlib import asynccontextmanager
from app.core import config, database, middleware
from app.routers import video, trainsettings = config.settings@asynccontextmanager
async def lifespan(app: FastAPI):# 初始化數據庫await database.init_db()yieldapp = FastAPI(lifespan=lifespan)# 注冊中間件
app.add_middleware(middleware.CORSMiddleware)
app.middleware("http")(middleware.log_request_middleware)# 掛載靜態目錄
app.mount("/static", StaticFiles(directory=settings.UPLOAD_DIR), name="static")# 包含路由
app.include_router(video.router, prefix='/api/video')
app.include_router(train.router, prefix='/api/train')if __name__ == '__main__':import uvicornuvicorn.run("app.main:app", host='0.0.0.0', port=4010, reload=True)

9. 中間件 (app/core/middleware.py)

import time
import logging
from fastapi.middleware.cors import CORSMiddleware
from fastapi import Request# 配置日志
logging.basicConfig(filename='request_logs.log', format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)def setup_cors(app):app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["GET", "POST"],allow_headers=["*"],)async def log_request_middleware(request: Request, call_next):start_time = time.time()response = await call_next(request)process_time = time.time() - start_timelog_message = (f"Method: {request.method}, Path: {request.url.path}, "f"Status: {response.status_code}, Time: {process_time:.2f}s, "f"URL: {request.url}")logging.info(log_message)return response

10. 環境文件 (.env)

# 數據庫配置
DATABASE_URL=mysql://user:password@localhost:3306/video_service# 文件配置
UPLOAD_DIR=static/videos
ALLOWED_EXTENSIONS=.mp4,.mov,.avi,.mkv,.webm
MAX_FILE_SIZE_MB=2000

適配已有數據庫表的要點

1. 禁用自動建表:

   # database.pygenerate_schemas=False  # 禁用自動創建表結構

2. 模型嚴格匹配表結構:

# models/video.pyclass VideoRecord(Model):# 字段與現有表嚴格對應class Meta:table = "video_records"  # 指定物理表名table_description = None  # 禁用ORM元數據修改

3. 手動驗證表結構:

在應用啟動后,建議運行以下腳本驗證模型與表結構一致性:

from app.models.video import VideoRecordfrom tortoise import run_asyncasync def check_table():conn = Tortoise.get_connection("default")columns = await conn.execute_query("DESCRIBE video_records")print("現有表字段:", [col['Field'] for col in columns])if __name__ == "__main__":run_async(check_table())

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92086.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92086.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92086.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

令牌桶限流算法

你提供的 Java 代碼實現的是令牌桶限流算法(Token Bucket Algorithm),這是目前最常用、最靈活的限流算法之一。它允許一定程度的“突發流量”,同時又能控制平均速率。下面我將:逐行詳細解釋 TokenBucketLimiter 類的每…

基于springboot的寵物商城設計與實現

管理員:登錄,個人中心,用戶管埋,寵物分類管理,寵物信息管理,留言反饋,寵物論壇,系統管理,訂單管理用戶:寵物信息,寵物論壇,公告信息&a…

Python day36

浙大疏錦行 Python day36. 復習日 本周內容: 如何導入模塊以及庫項目的規范拆分和寫法官方文檔的閱讀MLP神經網絡的訓練在GPU上訓練模型可視化以及推理

【gaussian-splatting】用自己的數據復現高斯潑濺(一)

1.環境準備1.1.下載diff-gaussian-rasterization這里本來沒啥說的,直接從github上下載就行了,但是我踩坑了,下的版本不對,后續運行報錯參數個數對不上,特在此給大家避坑,注意一定要下帶3dgs版本的diff-gaus…

中國移動h10g-01_S905L處理器安卓7.1當貝純凈版線刷機包帶root權限_融合終端網關

下載固件之前請先將主板上的屏蔽罩取下,查看處理器型號 是否為S905L型號,然后再下載固件進行刷機; 本頁面的固件是采用雙公頭數據線進行刷機的哈; 安卓4.4.2版本固件下載地址:點此進行下載 安卓7.1版本固件下載地址…

夜天之書 #110 涓滴開源:Cronexpr 的故事

在年初的一篇關于商業開源的博文當中,我介紹了在開發商業軟件的過程中,衍生出開源公共軟件庫的模式。在那篇博文里面,我只是簡單羅列了相關開源庫的名字及一句話總結。近期,我會結合商業開源實踐的最新進展,對其中一些…

完整的登陸學生管理系統(配置數據庫)

目錄 要求 思路 1. 登錄模塊(LoginFrame.java) 2. 學生信息管理模塊(StudentFrame.java) 3. 數據層(StudentDAO.java) 4. 業務層(StudentService.java / UserService.java) 5…

譯 | 在 Python 中從頭開始構建 Qwen-3 MoE

文章出自:基于 2個Expert 的 MoE 架構分步指南 本篇適合 MoE 架構初學者。文章亮點在于詳細拆解 Qwen 3 MoE 架構,并用簡單代碼從零實現 MoE 路由器、RMSNorm 等核心組件,便于理解內部原理。 該方法適用于需部署高性能、高效率大模型&#x…

Spring Boot + ShardingSphere 分庫分表實戰

🚀Spring Boot ShardingSphere 實戰:分庫分表,性能暴增的終極指南! ? 適用場景:千萬級大表、高并發、讀寫分離場景 ? 核心框架:Spring Boot 3.x ShardingSphere-JDBC 5.4.1 ? 數據庫:MySQL…

MaxKB 使用 MCP 連接 Oracle (免安裝 cx_Oracle 和 Oracle Instant Client)

一、背景 安裝cx_Oracle包和Oracle Instant Client來操作數據庫,比較繁瑣同時容易沖突,不同的 Oracle 版本都需要安裝不同的插件。這篇文章將介紹使用 MCP 協議的連接方法。 二、操作步驟 1、使用 1Panel 安裝 DBhub a) 數據庫類型選擇 Oracle 類型。…

基于Python的超聲波OFDM數字通信鏈路設計與實現

基于Python的超聲波OFDM數字通信鏈路設計與實現 摘要 本文詳細介紹了使用Python實現的超聲波OFDM(正交頻分復用)數字通信鏈路系統。該系統能夠在標準音響設備上運行,利用高于15kHz的超聲波頻段進行數據傳輸,采用48kHz采樣率。文章涵蓋了從OFDM基本原理、…

滑動窗口相關題目

近些年來,我國防沙治沙取得顯著成果。某沙漠新種植N棵胡楊(編號1-N),排成一排。一個月后,有M棵胡楊未能成活。現可補種胡楊K棵,請問如何補種(只能補種,不能新種)&#xf…

Java 工具類的“活化石”:Apache Commons 核心用法、性能陷阱與現代替代方案

在上一篇文章中,我們回顧了 Apache Commons 的經典組件。但作為 Java 世界中資歷最老、影響最深遠的工具庫,它的價值遠不止于此。許多開發者可能只使用了它 10% 的功能,卻忽略了另外 80% 能極大提升代碼質量的“隱藏寶石”。本文將提供一個更…

數據結構——圖及其C++實現 多源最短路徑 FloydWarshall算法

目錄 一、前言 二、算法思想 三、代碼實現 四、測試 五、源碼 一、前言 前兩篇學習的Dijkstra算法和Bellman-Ford算法都是用來求解圖的單源最短路徑,即從圖中指定的一個源點出發到圖中其他任意頂點的最短路徑。Dijkstra算法不能求解帶有負權重的圖的最短路徑&…

解決微軟應用商店 (Microsoft store) 打不開,無網絡連接的問題!

很多小伙伴都會遇見微軟應用商店 (Microsoft store)打開后出現無網絡的問題,一般出現這種問題基本都是因為你的電腦安裝了某些銀行的網銀工具,因為網銀工具為了安全會關閉Internet 選項中的最新版本的TLS協議,而微軟商店又需要最新的TLS協議才…

Android—服務+通知=>前臺服務

文章目錄1、Android服務1.1、定義1.2、基本用法1.2.1、定義一個服務1.2.2、服務注冊1.2.3、啟動和停止服務1.2.4、活動和服務進行通信1.3、帶綁定的服務示例1.3.1、定義服務類1.3.2、客戶端(Activity)綁定與交互?1.3.3、AndroidManifest.xml 注冊?1.3.…

從基礎功能到自主決策, Agent 開發進階路怎么走

Agent 開發進階路線大綱基礎功能實現核心模塊構建環境感知:傳感器數據處理(視覺、語音、文本等輸入)基礎動作控制:API調用、硬件驅動、簡單反饋機制狀態管理:有限狀態機(FSM)或行為樹&#xff0…

《動手學深度學習》讀書筆記—9.6編碼器-解碼器架構

本文記錄了自己在閱讀《動手學深度學習》時的一些思考,僅用來作為作者本人的學習筆記,不存在商業用途。 正如我們在9.5機器翻譯中所討論的,機器翻譯是序列轉換模型的一個核心問題,其輸入和輸出都是長度可變的序列。為了處理這種類…

DocBench:面向大模型文檔閱讀系統的評估基準與數據集分析

本文由「大千AI助手」原創發布,專注用真話講AI,回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我,一起撕掉過度包裝,學習真實的AI技術! 一、數據集概述與核心目標 DocBench 是由研究團隊于2024年提出的首個…

Python高級排序技術:非原生可比對象的自定義排序策略詳解

引言:超越原生比較操作的排序挑戰在Python數據處理中,我們經常需要處理不原生支持比較操作的對象。根據2024年《Python開發者生態系統報告》,在大型項目中,開發者平均需處理28%的自定義對象排序需求,這些對象包括&…