feat: 并发安全隔离 + 10分钟定时清理上传/输出目录
This commit is contained in:
@@ -1,14 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""vipsong Web — 音乐加密格式转码工具 (Web 版)。"""
|
||||
import os
|
||||
"""vipsong Web — 音乐加密格式转码工具 (Web 版)。
|
||||
|
||||
并发安全说明:
|
||||
- vipsong.convert_file() 会修改模块级全局 stats 字典,因此所有转码调用
|
||||
被互斥锁 (_convert_lock) 串行化,避免多线程/协程并发时统计数据错乱
|
||||
或输出路径冲突。
|
||||
- 每隔 10 分钟自动清扫 static/uploads 与 static/outputs 目录,释放磁盘。
|
||||
"""
|
||||
import shutil
|
||||
import threading
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
from fastapi import FastAPI, File, UploadFile, Form, HTTPException, BackgroundTasks
|
||||
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
|
||||
from fastapi import FastAPI, File, UploadFile, Form, HTTPException
|
||||
from fastapi.responses import HTMLResponse, FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
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
|
||||
UPLOAD_DIR = BASE_DIR / "static" / "uploads"
|
||||
@@ -55,12 +104,14 @@ async def convert(
|
||||
output_path = OUTPUT_DIR / output_name
|
||||
|
||||
try:
|
||||
vipsong.convert_file(
|
||||
str(upload_path),
|
||||
format,
|
||||
delete_flag=False,
|
||||
output_path=str(output_path),
|
||||
)
|
||||
# 加锁串行化: vipsong.convert_file() 修改全局 stats, 多线程下需隔离
|
||||
with _convert_lock:
|
||||
vipsong.convert_file(
|
||||
str(upload_path),
|
||||
format,
|
||||
delete_flag=False,
|
||||
output_path=str(output_path),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"转码失败: {e}")
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user