新增folder支持版本控制
This commit is contained in:
parent
09690a101e
commit
035b8338cc
106
fastapi_app.py
106
fastapi_app.py
@ -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)}")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user