feat: 并发安全隔离 + 10分钟定时清理上传/输出目录
This commit is contained in:
@@ -1,14 +1,63 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""vipsong Web — 音乐加密格式转码工具 (Web 版)。"""
|
"""vipsong Web — 音乐加密格式转码工具 (Web 版)。
|
||||||
import os
|
|
||||||
|
并发安全说明:
|
||||||
|
- vipsong.convert_file() 会修改模块级全局 stats 字典,因此所有转码调用
|
||||||
|
被互斥锁 (_convert_lock) 串行化,避免多线程/协程并发时统计数据错乱
|
||||||
|
或输出路径冲突。
|
||||||
|
- 每隔 10 分钟自动清扫 static/uploads 与 static/outputs 目录,释放磁盘。
|
||||||
|
"""
|
||||||
import shutil
|
import shutil
|
||||||
|
import threading
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi import FastAPI, File, UploadFile, Form, HTTPException, BackgroundTasks
|
from fastapi import FastAPI, File, UploadFile, Form, HTTPException
|
||||||
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
|
from fastapi.responses import HTMLResponse, FileResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
import vipsong
|
import vipsong
|
||||||
|
|
||||||
app = FastAPI(title="vipsong Web", description="音乐加密格式转码工具")
|
# --- 并发隔离 ---------------------------------------------
|
||||||
|
# vipsong.convert_file() 内部使用模块全局 stats, 需加锁串行化
|
||||||
|
_convert_lock = threading.Lock()
|
||||||
|
|
||||||
|
# --- 定期清理 -----------------------------------------------
|
||||||
|
_CLEANUP_INTERVAL = 10 * 60 # 10 分钟
|
||||||
|
_cleanup_stop = threading.Event()
|
||||||
|
|
||||||
|
|
||||||
|
def _purge_directory(directory: Path):
|
||||||
|
"""删除目录内所有文件(保留目录本身)。"""
|
||||||
|
if not directory.exists():
|
||||||
|
return
|
||||||
|
for entry in directory.iterdir():
|
||||||
|
try:
|
||||||
|
if entry.is_file() or entry.is_symlink():
|
||||||
|
entry.unlink(missing_ok=True)
|
||||||
|
elif entry.is_dir():
|
||||||
|
shutil.rmtree(entry, ignore_errors=True)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanup_loop():
|
||||||
|
"""后台线程:每隔 _CLEANUP_INTERVAL 清扫上传和输出目录。"""
|
||||||
|
while not _cleanup_stop.wait(_CLEANUP_INTERVAL):
|
||||||
|
_purge_directory(UPLOAD_DIR)
|
||||||
|
_purge_directory(OUTPUT_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
# 启动清理线程
|
||||||
|
t = threading.Thread(target=_cleanup_loop, daemon=True, name="vipsong-cleanup")
|
||||||
|
t.start()
|
||||||
|
yield
|
||||||
|
# 关闭时停止清理线程
|
||||||
|
_cleanup_stop.set()
|
||||||
|
t.join(timeout=5)
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(title="vipsong Web", description="音乐加密格式转码工具", lifespan=lifespan)
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).parent
|
BASE_DIR = Path(__file__).parent
|
||||||
UPLOAD_DIR = BASE_DIR / "static" / "uploads"
|
UPLOAD_DIR = BASE_DIR / "static" / "uploads"
|
||||||
@@ -55,6 +104,8 @@ async def convert(
|
|||||||
output_path = OUTPUT_DIR / output_name
|
output_path = OUTPUT_DIR / output_name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# 加锁串行化: vipsong.convert_file() 修改全局 stats, 多线程下需隔离
|
||||||
|
with _convert_lock:
|
||||||
vipsong.convert_file(
|
vipsong.convert_file(
|
||||||
str(upload_path),
|
str(upload_path),
|
||||||
format,
|
format,
|
||||||
|
|||||||
Reference in New Issue
Block a user