新增folder支持版本控制

This commit is contained in:
朱潮 2025-11-09 12:37:18 +08:00
parent 09690a101e
commit 035b8338cc

View File

@ -8,9 +8,10 @@ import requests
import aiohttp import aiohttp
from typing import AsyncGenerator, Dict, List, Optional, Union, Any from typing import AsyncGenerator, Dict, List, Optional, Union, Any
from datetime import datetime from datetime import datetime
import re
import uvicorn import uvicorn
from fastapi import FastAPI, HTTPException, Depends, Header, UploadFile, File from fastapi import FastAPI, HTTPException, Depends, Header, UploadFile, File, Form
from fastapi.responses import StreamingResponse, HTMLResponse, FileResponse from fastapi.responses import StreamingResponse, HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@ -51,10 +52,69 @@ from modified_assistant import update_agent_llm
from task_queue.manager import queue_manager from task_queue.manager import queue_manager
from task_queue.integration_tasks import process_files_async, process_files_incremental_async, cleanup_project_async from task_queue.integration_tasks import process_files_async, process_files_incremental_async, cleanup_project_async
from task_queue.task_status import task_status_store from task_queue.task_status import task_status_store
import re
os.environ["TOKENIZERS_PARALLELISM"] = "false" os.environ["TOKENIZERS_PARALLELISM"] = "false"
def get_versioned_filename(upload_dir: str, name_without_ext: str, file_extension: str) -> tuple[str, int]:
"""
获取带版本号的文件名自动处理文件删除和版本递增
Args:
upload_dir: 上传目录路径
name_without_ext: 不含扩展名的文件名
file_extension: 文件扩展名包含点号
Returns:
tuple[str, int]: (最终文件名, 版本号)
"""
# 检查原始文件是否存在
original_file = os.path.join(upload_dir, name_without_ext + file_extension)
original_exists = os.path.exists(original_file)
# 查找所有相关的版本化文件
pattern = re.compile(re.escape(name_without_ext) + r'_(\d+)' + re.escape(file_extension) + r'$')
existing_versions = []
files_to_delete = []
for filename in os.listdir(upload_dir):
# 检查是否是原始文件
if filename == name_without_ext + file_extension:
files_to_delete.append(filename)
continue
# 检查是否是版本化文件
match = pattern.match(filename)
if match:
version_num = int(match.group(1))
existing_versions.append(version_num)
files_to_delete.append(filename)
# 如果没有任何相关文件存在使用原始文件名版本1
if not original_exists and not existing_versions:
return name_without_ext + file_extension, 1
# 删除所有现有文件(原始文件和版本化文件)
for filename in files_to_delete:
file_to_delete = os.path.join(upload_dir, filename)
try:
os.remove(file_to_delete)
print(f"已删除文件: {file_to_delete}")
except OSError as e:
print(f"删除文件失败 {file_to_delete}: {e}")
# 确定下一个版本号
if existing_versions:
next_version = max(existing_versions) + 1
else:
next_version = 2
# 生成带版本号的文件名
versioned_filename = f"{name_without_ext}_{next_version}{file_extension}"
return versioned_filename, next_version
# Custom version for qwen-agent messages - keep this function as it's specific to this app # Custom version for qwen-agent messages - keep this function as it's specific to this app
def get_content_from_messages(messages: List[dict], tool_response: bool = True) -> str: def get_content_from_messages(messages: List[dict], tool_response: bool = True) -> str:
"""Extract content from qwen-agent messages with special formatting""" """Extract content from qwen-agent messages with special formatting"""
@ -987,11 +1047,12 @@ async def chat_completions_v2(request: ChatRequestV2, authorization: Optional[st
@app.post("/api/v1/upload") @app.post("/api/v1/upload")
async def upload_file(file: UploadFile = File(...), folder: Optional[str] = None): async def upload_file(file: UploadFile = File(...), folder: Optional[str] = Form(None)):
""" """
文件上传API接口上传文件到 ./projects/uploads/ 目录下 文件上传API接口上传文件到 ./projects/uploads/ 目录下
可以指定自定义文件夹名如果不指定则使用日期文件夹 可以指定自定义文件夹名如果不指定则使用日期文件夹
指定文件夹时使用原始文件名并支持版本控制
Args: Args:
file: 上传的文件 file: 上传的文件
@ -1001,6 +1062,10 @@ async def upload_file(file: UploadFile = File(...), folder: Optional[str] = None
dict: 包含文件路径和文件夹信息的响应 dict: 包含文件路径和文件夹信息的响应
""" """
try: try:
# 调试信息
print(f"Received folder parameter: {folder}")
print(f"File received: {file.filename if file else 'None'}")
# 确定上传文件夹 # 确定上传文件夹
if folder: if folder:
# 使用指定的自定义文件夹 # 使用指定的自定义文件夹
@ -1016,8 +1081,34 @@ async def upload_file(file: UploadFile = File(...), folder: Optional[str] = None
upload_dir = os.path.join("projects", "uploads", target_folder) upload_dir = os.path.join("projects", "uploads", target_folder)
os.makedirs(upload_dir, exist_ok=True) os.makedirs(upload_dir, exist_ok=True)
# 生成唯一文件名 # 处理文件名
file_extension = os.path.splitext(file.filename)[1] if file.filename else "" if not file.filename:
raise HTTPException(status_code=400, detail="文件名不能为空")
# 解析文件名和扩展名
original_filename = file.filename
name_without_ext, file_extension = os.path.splitext(original_filename)
# 根据是否指定文件夹决定命名策略
if folder:
# 使用原始文件名,支持版本控制
final_filename, version = get_versioned_filename(upload_dir, name_without_ext, file_extension)
file_path = os.path.join(upload_dir, final_filename)
# 保存文件
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {
"success": True,
"message": f"文件上传成功{' (版本: ' + str(version) + ')' if version > 1 else ''}",
"file_path": file_path,
"folder": target_folder,
"original_filename": original_filename,
"version": version
}
else:
# 使用UUID唯一文件名原有逻辑
unique_filename = f"{uuid.uuid4()}{file_extension}" unique_filename = f"{uuid.uuid4()}{file_extension}"
file_path = os.path.join(upload_dir, unique_filename) file_path = os.path.join(upload_dir, unique_filename)
@ -1029,9 +1120,12 @@ async def upload_file(file: UploadFile = File(...), folder: Optional[str] = None
"success": True, "success": True,
"message": "文件上传成功", "message": "文件上传成功",
"file_path": file_path, "file_path": file_path,
"folder": target_folder "folder": target_folder,
"original_filename": original_filename
} }
except HTTPException:
raise
except Exception as e: except Exception as e:
print(f"Error uploading file: {str(e)}") print(f"Error uploading file: {str(e)}")
raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}") raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}")