refactor: 更新 MinIO 存储逻辑,优化文件路径和桶名称(不兼容)

This commit is contained in:
Wenjie Zhang 2026-03-18 00:54:27 +08:00
parent fe2153ad76
commit 095f0db8e6
11 changed files with 177 additions and 113 deletions

View File

@ -172,6 +172,7 @@ async def text_to_img_qwen_image(
negative_prompt: Annotated[str, "负面提示词,用于指定不想出现在图片中的元素"] = "",
num_inference_steps: Annotated[int, "推理步数范围1-100"] = 20,
guidance_scale: Annotated[float, "引导强度,控制图片与提示词的匹配程度"] = 7.5,
user_id: Annotated[str, "用户ID用于图片归档路径"] = "unknown",
) -> str:
"""使用 Qwen-Image 模型生成图片返回图片的URL需要注意的是生成结果不会默认展示需要将返回的URL进行展示处理。"""
url = "https://api.siliconflow.cn/v1/images/generations"
@ -202,9 +203,10 @@ async def text_to_img_qwen_image(
response = requests.get(image_url)
file_data = response.content
file_name = f"{uuid.uuid4()}.jpg"
safe_user_id = str(user_id or "unknown").replace("/", "_").replace("\\", "_")
file_name = f"user/{safe_user_id}/generated-images/{uuid.uuid4()}.jpg"
image_url = await aupload_file_to_minio(
bucket_name="generated-images", file_name=file_name, data=file_data, file_extension="jpg"
bucket_name="public", file_name=file_name, data=file_data, file_extension="jpg"
)
logger.info(f"Image uploaded. URL: {image_url}")
return image_url

View File

@ -365,14 +365,10 @@ class KnowledgeBase(ABC):
from yuxi.storage.minio import get_minio_client
minio_client = get_minio_client()
bucket_name = "kb-documents" # Or reuse existing bucket strategy?
# Maybe store in 'kb-files' or 'kb-markdowns'
# Current uploads go to 'kb-files' usually?
# Let's use 'kb-parsed'
bucket_name = "kb-parsed"
bucket_name = minio_client.KB_BUCKETS["parsed"]
await asyncio.to_thread(minio_client.ensure_bucket_exists, bucket_name)
object_name = f"{db_id}/{file_id}/parsed.md"
object_name = f"{db_id}/parsed/{file_id}.md"
data = content.encode("utf-8")
# Return standard HTTP URL from UploadResult
@ -494,22 +490,23 @@ class KnowledgeBase(ABC):
except Exception as e:
logger.warning(f"Failed to delete MinIO file {file_path}: {e}")
# 删除解析后的 markdown 文件 (kb-parsed/{db_id}/{file_id}/parsed.md)
parsed_object = f"{db_id}/{file_id}/parsed.md"
# 删除解析后的 markdown 文件
parsed_object = f"{db_id}/parsed/{file_id}.md"
await minio_client.adelete_file(minio_client.KB_BUCKETS["parsed"], parsed_object)
del self.files_meta[file_id]
# 2. 并行删除所有知识库 bucket 中该 db_id 下的文件
prefix = f"{db_id}/"
await asyncio.gather(
minio_client.adelete_objects_by_prefix(minio_client.KB_BUCKETS["parsed"], prefix),
minio_client.adelete_objects_by_prefix(minio_client.KB_BUCKETS["documents"], prefix),
minio_client.adelete_objects_by_prefix(minio_client.KB_BUCKETS["images"], prefix),
# 直接删除 ref bucket两种命名格式
minio_client.adelete_bucket(minio_client.get_ref_bucket_name(db_id)),
minio_client.adelete_bucket(minio_client.get_ref_bucket_name_full(db_id)),
)
cleanup_buckets = {
minio_client.KB_BUCKETS["parsed"],
minio_client.KB_BUCKETS["documents"],
minio_client.KB_BUCKETS["images"],
}
cleanup_tasks = [
minio_client.adelete_objects_by_prefix(bucket_name, prefix) for bucket_name in cleanup_buckets
]
await asyncio.gather(*cleanup_tasks)
# 3. 删除数据库记录
del self.databases_meta[db_id]

View File

@ -22,7 +22,7 @@ from markdownify import markdownify as md_convert
from yuxi.plugins.parser.zip_utils import process_zip_file as _process_zip_file
from yuxi.storage.minio import get_minio_client
from yuxi.utils import hashstr, logger
from yuxi.utils import logger
SUPPORTED_FILE_EXTENSIONS: tuple[str, ...] = (
".txt",
@ -69,14 +69,32 @@ def _get_docling_converter() -> DocumentConverter:
return _docling_converter
def _upload_image_to_minio(image_data: bytes, filename: str, db_id: str) -> str:
def _resolve_image_storage_params(params: dict | None) -> tuple[str, str]:
params = params or {}
image_bucket = params.get("image_bucket") or "public"
image_prefix = params.get("image_prefix")
if image_prefix:
normalized_prefix = str(image_prefix).strip("/")
if normalized_prefix:
return image_bucket, normalized_prefix
db_id = params.get("db_id")
if db_id:
return image_bucket, f"{db_id}/kb-images"
return image_bucket, "unknown/kb-images"
def _upload_image_to_minio(image_data: bytes, filename: str, bucket_name: str, object_prefix: str) -> str:
"""上传图片到 MinIO返回 URL"""
minio_client = get_minio_client()
minio_client.ensure_bucket_exists("kb-images")
file_id = hashstr(filename, length=16)
minio_client.ensure_bucket_exists(bucket_name)
normalized_prefix = object_prefix.strip("/") or "unknown/kb-images"
timestamp = int(time.time() * 1000000)
suffix = Path(filename).suffix.lower()
object_name = f"{db_id}/{file_id}/images/{timestamp}_{Path(filename).name}"
object_name = f"{normalized_prefix}/{timestamp}_{Path(filename).name}"
content_type_map = {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
@ -90,7 +108,7 @@ def _upload_image_to_minio(image_data: bytes, filename: str, db_id: str) -> str:
content_type = content_type_map.get(suffix, "image/jpeg")
result = minio_client.upload_file(
bucket_name="kb-images",
bucket_name=bucket_name,
object_name=object_name,
data=image_data,
content_type=content_type,
@ -112,13 +130,13 @@ def _convert_with_docling(file_path: Path, params: dict | None = None) -> str:
Args:
file_path: 文件路径
params: 参数包含 db_id 用于图片上传
params: 参数可包含 image_bucket/image_prefix
Returns:
Markdown 字符串
"""
params = params or {}
db_id = params.get("db_id") or "docling-docs"
image_bucket, image_prefix = _resolve_image_storage_params(params)
converter = _get_docling_converter()
result = converter.convert(file_path)
@ -145,7 +163,7 @@ def _convert_with_docling(file_path: Path, params: dict | None = None) -> str:
image_urls: list[str] = []
for filename, image_data in image_refs:
try:
url = _upload_image_to_minio(image_data, filename, db_id)
url = _upload_image_to_minio(image_data, filename, image_bucket, image_prefix)
image_urls.append(f"![{filename}]({url})")
except Exception as e:
logger.error(f"上传图片失败 {filename}: {e}")
@ -334,6 +352,10 @@ def parse_pdf(file, params=None):
if opt_ocr == "disable":
return pdfreader(file, params=params)
image_bucket, image_prefix = _resolve_image_storage_params(params)
params.setdefault("image_bucket", image_bucket)
params.setdefault("image_prefix", image_prefix)
try:
return DocumentProcessorFactory.process_file(opt_ocr, file, params)
@ -373,6 +395,10 @@ def parse_image(file, params=None):
"请选择OCR方式 (rapid_ocr/mineru_ocr/mineru_official/pp_structure_v3_ocr) 或移除该文件。"
)
image_bucket, image_prefix = _resolve_image_storage_params(params)
params.setdefault("image_bucket", image_bucket)
params.setdefault("image_prefix", image_prefix)
try:
return DocumentProcessorFactory.process_file(opt_ocr, file, params)
@ -543,14 +569,19 @@ async def process_file_to_markdown(file_path: str, params: dict | None = None) -
result = f"```json\n{json_str}\n```"
elif file_ext == ".zip":
if not params or "db_id" not in params:
raise ValueError("ZIP文件处理需要在params中提供db_id参数")
zip_result = await _process_zip_file(str(file_path_obj), params["db_id"])
image_bucket, image_prefix = _resolve_image_storage_params(params)
zip_result = await _process_zip_file(
str(file_path_obj),
image_bucket=image_bucket,
image_prefix=image_prefix,
)
# 将处理结果保存到params中供调用方使用
params["_zip_images_info"] = zip_result["images_info"]
params["_zip_content_hash"] = zip_result["content_hash"]
if params is not None:
params["_zip_images_info"] = zip_result["images_info"]
params["_zip_content_hash"] = zip_result["content_hash"]
params["_zip_image_bucket"] = image_bucket
params["_zip_image_prefix"] = image_prefix
result = zip_result["markdown_content"]

View File

@ -189,7 +189,14 @@ class MinerUParser(BaseDocumentProcessor):
tmp_zip.flush()
try:
processed = process_zip_file_sync(tmp_zip.name, params.get("db_id") or "ocr-temp")
image_bucket = params.get("image_bucket") or "public"
image_prefix = params.get("image_prefix") or "unknown/kb-images"
processed = process_zip_file_sync(
tmp_zip.name,
image_bucket=image_bucket,
image_prefix=image_prefix,
)
text = processed["markdown_content"]
finally:
os.unlink(tmp_zip.name)

View File

@ -156,7 +156,14 @@ class MinerUOfficialParser(BaseDocumentProcessor):
return text
try:
processed = process_zip_file_sync(zip_path, params.get("db_id") or "ocr-test")
image_bucket = params.get("image_bucket") or "public"
image_prefix = params.get("image_prefix") or "unknown/kb-images"
processed = process_zip_file_sync(
zip_path,
image_bucket=image_bucket,
image_prefix=image_prefix,
)
text = processed["markdown_content"]
except Exception:
import zipfile

View File

@ -7,16 +7,29 @@ import zipfile
from pathlib import Path
from yuxi.storage.minio import get_minio_client
from yuxi.utils import hashstr, logger
from yuxi.utils import logger
DEFAULT_IMAGE_BUCKET = "public"
DEFAULT_IMAGE_PREFIX = "unknown/kb-images"
async def process_zip_file(zip_path: str, db_id: str) -> dict:
def _normalize_object_prefix(prefix: str | None) -> str:
normalized = (prefix or DEFAULT_IMAGE_PREFIX).strip("/")
return normalized or DEFAULT_IMAGE_PREFIX
async def process_zip_file(
zip_path: str,
image_bucket: str = DEFAULT_IMAGE_BUCKET,
image_prefix: str = DEFAULT_IMAGE_PREFIX,
) -> dict:
"""
处理ZIP文件提取markdown内容和图片
Args:
zip_path: ZIP文件路径
db_id: 数据库ID
image_bucket: 图片上传的目标 bucket
image_prefix: 图片上传对象前缀
Returns:
dict: {
@ -43,9 +56,15 @@ async def process_zip_file(zip_path: str, db_id: str) -> dict:
images_info = []
images_dir = find_images_directory(zf, md_file)
normalized_prefix = _normalize_object_prefix(image_prefix)
if images_dir:
images_info = await process_images(zf, images_dir, db_id, md_file)
images_info = await process_images(
zf,
images_dir,
image_bucket=image_bucket,
image_prefix=normalized_prefix,
)
markdown_content = replace_image_links(markdown_content, images_info)
content_hash = hashlib.sha256(markdown_content.encode("utf-8")).hexdigest()
@ -57,12 +76,16 @@ async def process_zip_file(zip_path: str, db_id: str) -> dict:
}
def process_zip_file_sync(zip_path: str, db_id: str) -> dict:
def process_zip_file_sync(
zip_path: str,
image_bucket: str = DEFAULT_IMAGE_BUCKET,
image_prefix: str = DEFAULT_IMAGE_PREFIX,
) -> dict:
"""同步调用 ZIP 处理,供同步解析器使用。"""
try:
asyncio.get_running_loop()
except RuntimeError:
return asyncio.run(process_zip_file(zip_path, db_id))
return asyncio.run(process_zip_file(zip_path, image_bucket=image_bucket, image_prefix=image_prefix))
result: dict | None = None
error: Exception | None = None
@ -70,7 +93,7 @@ def process_zip_file_sync(zip_path: str, db_id: str) -> dict:
def runner() -> None:
nonlocal result, error
try:
result = asyncio.run(process_zip_file(zip_path, db_id))
result = asyncio.run(process_zip_file(zip_path, image_bucket=image_bucket, image_prefix=image_prefix))
except Exception as exc: # pragma: no cover - pass through outer raise
error = exc
@ -106,7 +129,12 @@ def find_images_directory(zip_file: zipfile.ZipFile, md_file_path: str) -> str |
return None
async def process_images(zip_file: zipfile.ZipFile, images_dir: str, db_id: str, md_file_path: str) -> list[dict]:
async def process_images(
zip_file: zipfile.ZipFile,
images_dir: str,
image_bucket: str,
image_prefix: str,
) -> list[dict]:
"""处理图片上传到MinIO并返回信息"""
supported_extensions = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}
content_type_map = {
@ -120,12 +148,10 @@ async def process_images(zip_file: zipfile.ZipFile, images_dir: str, db_id: str,
images = []
image_names = [n for n in zip_file.namelist() if n.startswith(images_dir + "/")]
normalized_prefix = _normalize_object_prefix(image_prefix)
minio_client = get_minio_client()
bucket_name = "kb-images"
await asyncio.to_thread(minio_client.ensure_bucket_exists, bucket_name)
file_id = hashstr(Path(md_file_path).name, length=16)
await asyncio.to_thread(minio_client.ensure_bucket_exists, image_bucket)
for img_name in image_names:
suffix = Path(img_name).suffix.lower()
@ -137,11 +163,11 @@ async def process_images(zip_file: zipfile.ZipFile, images_dir: str, db_id: str,
data = f.read()
timestamp = int(time.time() * 1000000)
object_name = f"{db_id}/{file_id}/images/{timestamp}_{Path(img_name).name}"
object_name = f"{normalized_prefix}/{timestamp}_{Path(img_name).name}"
content_type = content_type_map.get(suffix, "image/jpeg")
result = await minio_client.aupload_file(
bucket_name=bucket_name,
bucket_name=image_bucket,
object_name=object_name,
data=data,
content_type=content_type,

View File

@ -15,7 +15,7 @@ from yuxi.utils.datetime_utils import utc_isoformat
from yuxi.utils.logging_config import logger
# 附件存储桶名称
ATTACHMENTS_BUCKET = "chat-attachments"
ATTACHMENTS_BUCKET = "user"
async def require_user_conversation(conv_repo: ConversationRepository, thread_id: str, user_id: str):
@ -246,14 +246,15 @@ async def upload_thread_attachment_view(
file_content = await file.read()
await file.seek(0)
client = get_minio_client()
object_name = f"attachments/{thread_id}/{conversion.file_name}"
safe_filename = conversion.file_name.replace("/", "_").replace("\\", "_")
object_name = f"user/{current_user_id}/chat_attachments/{thread_id}/{safe_filename}"
result = client.upload_file(
bucket_name=ATTACHMENTS_BUCKET,
object_name=object_name,
data=file_content,
content_type=conversion.file_type or "application/octet-stream",
)
minio_url = result.public_url
minio_url = result.url
logger.info(f"Uploaded attachment to MinIO: {object_name}")
except Exception as e:
logger.error(f"Failed to upload attachment to MinIO: {e}")

View File

@ -41,25 +41,15 @@ class MinIOClient:
简化的 MinIO 客户端类
"""
PUBLIC_READ_BUCKETS = {"generated-images", "avatar", "kb-images"}
PUBLIC_READ_BUCKETS = {"public"}
# 知识库相关的 bucket 名称
KB_BUCKETS = {
"documents": "kb-documents",
"parsed": "kb-parsed",
"images": "kb-images",
"documents": "knowledgebases",
"parsed": "knowledgebases",
"images": "public",
}
@staticmethod
def get_ref_bucket_name(db_id: str) -> str:
"""获取 ref bucket 名称截断32位"""
return f"ref-{db_id[:32].replace('_', '-')}"
@staticmethod
def get_ref_bucket_name_full(db_id: str) -> str:
"""获取 ref bucket 名称(不截断)"""
return f"ref-{db_id.replace('_', '-')}"
def __init__(self):
"""初始化 MinIO 客户端"""
self.endpoint = os.getenv("MINIO_URI") or "http://milvus-minio:9000"

View File

@ -757,8 +757,8 @@ async def upload_user_avatar(
file_extension = file.filename.split(".")[-1].lower() if file.filename and "." in file.filename else "jpg"
# 上传到MinIO
file_name = f"{uuid.uuid4()}.{file_extension}"
avatar_url = await aupload_file_to_minio("avatar", file_name, file_content, file_extension)
file_name = f"avatar/{current_user.id}/{uuid.uuid4()}.{file_extension}"
avatar_url = await aupload_file_to_minio("public", file_name, file_content, file_extension)
# 更新用户头像
current_user.avatar = avatar_url

View File

@ -16,6 +16,7 @@ from yuxi import config, knowledge_base
from yuxi.knowledge.chunking.ragflow_like.presets import ensure_chunk_defaults_in_additional_params
from yuxi.knowledge.indexing import SUPPORTED_FILE_EXTENSIONS, is_supported_file_extension, process_file_to_markdown
from yuxi.knowledge.utils import calculate_content_hash
from yuxi.knowledge.utils.kb_utils import parse_minio_url
from yuxi.models.embed import test_all_embedding_models_status, test_embedding_model_status
from yuxi.storage.postgres.models_business import User
from yuxi.storage.minio.client import MinIOClient, StorageError, aupload_file_to_minio, get_minio_client
@ -680,15 +681,18 @@ async def batch_delete_documents(
deleted_count += 1
continue
file_name = file_meta_info.get("meta", {}).get("filename")
file_path = file_meta_info.get("meta", {}).get("path", "")
# 尝试从MinIO删除文件如果失败例如旧知识库没有MinIO实例则忽略
# 尝试从 MinIO 删除文件对象与解析结果
try:
minio_client = get_minio_client()
await minio_client.adelete_file(MinIOClient.get_ref_bucket_name(db_id), file_name)
logger.debug(f"成功从MinIO删除文件: {file_name}")
if file_path.startswith(("http://", "https://")):
bucket_name, object_name = parse_minio_url(file_path)
await minio_client.adelete_file(bucket_name, object_name)
await minio_client.adelete_file(minio_client.KB_BUCKETS["parsed"], f"{db_id}/parsed/{doc_id}.md")
logger.debug(f"成功从MinIO删除文件: {file_path}")
except Exception as minio_error:
logger.warning(f"从MinIO删除文件失败可能是旧知识库: {minio_error}")
logger.warning(f"从MinIO删除文件失败: {minio_error}")
# 无论MinIO删除是否成功都继续从知识库删除
await knowledge_base.delete_file(db_id, doc_id)
@ -723,17 +727,18 @@ async def delete_document(db_id: str, doc_id: str, current_user: User = Depends(
await knowledge_base.delete_folder(db_id, doc_id)
return {"message": "文件夹删除成功"}
file_name = file_meta_info.get("meta", {}).get("path", "").split("/")[-1]
file_path = file_meta_info.get("meta", {}).get("path", "")
# 尝试从MinIO删除文件如果失败例如旧知识库没有MinIO实例则忽略
# 尝试从 MinIO 删除文件对象与解析结果
try:
minio_client = get_minio_client()
await minio_client.adelete_file(MinIOClient.get_ref_bucket_name(db_id), file_name)
# 同时删除 parsed bucket 中的 parsed.md 文件
await minio_client.adelete_file(minio_client.KB_BUCKETS["parsed"], f"{db_id}/{doc_id}/parsed.md")
logger.debug(f"成功从MinIO删除文件: {file_name}")
if file_path.startswith(("http://", "https://")):
bucket_name, object_name = parse_minio_url(file_path)
await minio_client.adelete_file(bucket_name, object_name)
await minio_client.adelete_file(minio_client.KB_BUCKETS["parsed"], f"{db_id}/parsed/{doc_id}.md")
logger.debug(f"成功从MinIO删除文件: {file_path}")
except Exception as minio_error:
logger.warning(f"从MinIO删除文件失败(可能是旧知识库): {minio_error}")
logger.warning(f"从MinIO删除文件失败: {minio_error}")
# 无论MinIO删除是否成功都继续从知识库删除
await knowledge_base.delete_file(db_id, doc_id)
@ -1240,13 +1245,11 @@ async def fetch_url(
# 3. 上传到 MinIO
minio_client = get_minio_client()
# 确保存储 bucket 存在
bucket_name = "kb-html-archives"
bucket_name = MinIOClient.KB_BUCKETS["documents"]
await asyncio.to_thread(minio_client.ensure_bucket_exists, bucket_name)
# 如果没有提供 db_id使用 default
folder = db_id if db_id else "default"
object_name = f"{folder}/{content_hash}.html"
folder = db_id if db_id else "unknown"
object_name = f"{folder}/upload/{content_hash}.html"
upload_result = await minio_client.aupload_file(
bucket_name=bucket_name,
@ -1326,14 +1329,12 @@ async def upload_file(
timestamp = int(time.time() * 1000)
minio_filename = f"{basename}_{timestamp}{ext}"
# 生成符合MinIO规范的存储桶名称将下划线替换为连字符截取长度以满足MinIO的63字符限制
if db_id:
bucket_name = f"ref-{db_id[:32].replace('_', '-')}"
else:
bucket_name = "default-uploads"
bucket_name = MinIOClient.KB_BUCKETS["documents"]
folder = db_id if db_id else "unknown"
object_name = f"{folder}/upload/{minio_filename}"
# 上传到MinIO
minio_url = await aupload_file_to_minio(bucket_name, minio_filename, file_bytes, ext.lstrip("."))
minio_url = await aupload_file_to_minio(bucket_name, object_name, file_bytes, ext.lstrip("."))
# 检测同名文件(基于原始文件名)
same_name_files = await knowledge_base.get_same_name_files(db_id, filename)
@ -1348,6 +1349,7 @@ async def upload_file(
"filename": filename, # 原始文件名(小写)
"original_filename": basename, # 原始文件名(去掉后缀)
"minio_filename": minio_filename, # MinIO中的文件名带时间戳
"object_name": object_name,
"bucket_name": bucket_name, # MinIO存储桶名称
"same_name_files": same_name_files, # 同名文件列表
"has_same_name": has_same_name, # 是否包含同名文件标志

View File

@ -24,6 +24,8 @@ async def test_delete_knowledge_base_cleanup():
# 初始化
minio_client = get_minio_client()
kb_manager = knowledge_base
doc_bucket = minio_client.KB_BUCKETS["documents"]
parsed_bucket = minio_client.KB_BUCKETS["parsed"]
# 测试参数
test_db_name = "test_cleanup_db"
@ -44,35 +46,35 @@ async def test_delete_knowledge_base_cleanup():
print("2. 上传测试文件到 MinIO...")
# 确保 bucket 存在
minio_client.ensure_bucket_exists("kb-documents")
minio_client.ensure_bucket_exists("kb-parsed")
minio_client.ensure_bucket_exists(doc_bucket)
minio_client.ensure_bucket_exists(parsed_bucket)
# 上传到 kb-documents (原始文件)
test_object_name = f"{db_id}/test_file_{db_id[:8]}.txt"
# 上传到知识库文档路径 (knowledgebases/{db_id}/upload/...)
test_object_name = f"{db_id}/upload/test_file_{db_id[:8]}.txt"
minio_client.upload_file(
bucket_name="kb-documents",
bucket_name=doc_bucket,
object_name=test_object_name,
data=test_file_content,
content_type="text/plain",
)
print(f" 文件上传到 kb-documents: {test_object_name}")
print(f" 文件上传到 {doc_bucket}: {test_object_name}")
# 上传到 kb-parsed (解析后的 markdown)
# 上传到知识库解析路径 (knowledgebases/{db_id}/parsed/...)
test_file_id = f"file_test_{db_id[:8]}"
parsed_object_name = f"{db_id}/{test_file_id}/parsed.md"
parsed_object_name = f"{db_id}/parsed/{test_file_id}.md"
parsed_content = b"# Test Parsed Content\n\nThis is parsed markdown."
minio_client.upload_file(
bucket_name="kb-parsed",
bucket_name=parsed_bucket,
object_name=parsed_object_name,
data=parsed_content,
content_type="text/markdown",
)
print(f" 文件上传到 kb-parsed: {parsed_object_name}")
print(f" 文件上传到 {parsed_bucket}: {parsed_object_name}")
# 3. 验证文件存在
print("3. 验证文件存在...")
assert minio_client.file_exists("kb-documents", test_object_name), "原始文件应该存在"
assert minio_client.file_exists("kb-parsed", parsed_object_name), "解析后文件应该存在"
assert minio_client.file_exists(doc_bucket, test_object_name), "原始文件应该存在"
assert minio_client.file_exists(parsed_bucket, parsed_object_name), "解析后文件应该存在"
print(" 文件存在验证通过")
# 4. 删除知识库
@ -83,13 +85,13 @@ async def test_delete_knowledge_base_cleanup():
# 5. 验证 MinIO 文件已清理
print("5. 验证 MinIO 文件已清理...")
# 检查 kb-documents 中的文件
doc_exists = minio_client.file_exists("kb-documents", test_object_name)
print(f" kb-documents/{test_object_name} 存在: {doc_exists}")
# 检查文档对象
doc_exists = minio_client.file_exists(doc_bucket, test_object_name)
print(f" {doc_bucket}/{test_object_name} 存在: {doc_exists}")
# 检查 kb-parsed 中的文件
parsed_exists = minio_client.file_exists("kb-parsed", parsed_object_name)
print(f" kb-parsed/{parsed_object_name} 存在: {parsed_exists}")
# 检查解析对象
parsed_exists = minio_client.file_exists(parsed_bucket, parsed_object_name)
print(f" {parsed_bucket}/{parsed_object_name} 存在: {parsed_exists}")
# 6. 输出测试结果
print("\n" + "=" * 50)
@ -99,9 +101,9 @@ async def test_delete_knowledge_base_cleanup():
else:
print("❌ 测试失败!以下文件未被清理:")
if doc_exists:
print(f" - kb-documents/{test_object_name}")
print(f" - {doc_bucket}/{test_object_name}")
if parsed_exists:
print(f" - kb-parsed/{parsed_object_name}")
print(f" - {parsed_bucket}/{parsed_object_name}")
return False
except Exception as e:
@ -116,9 +118,8 @@ async def test_delete_knowledge_base_cleanup():
print("\n清理测试数据...")
try:
# 尝试删除可能残留的文件(忽略错误)
for obj in [test_object_name, parsed_object_name]:
for bucket, obj in [(doc_bucket, test_object_name), (parsed_bucket, parsed_object_name)]:
try:
bucket = "kb-documents" if "kb-documents" in str(obj) else "kb-parsed"
await minio_client.adelete_file(bucket, obj)
except Exception:
pass