From a0194b8e09f939063eb2ed89d35ab737306c94d0 Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Thu, 16 Oct 2025 14:12:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20MinIO=20=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E5=AD=98=E5=82=A8=E6=A1=B6=E5=B9=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E8=AF=BB=E5=8F=96=E7=AD=96=E7=95=A5=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/auth_router.py | 2 +- src/agents/chatbot/tools.py | 2 +- src/storage/minio/client.py | 54 ++++++++++++++++++++++++++++++++--- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/server/routers/auth_router.py b/server/routers/auth_router.py index ba18ad0f..c0b57d13 100644 --- a/server/routers/auth_router.py +++ b/server/routers/auth_router.py @@ -603,7 +603,7 @@ async def upload_user_avatar( file_extension = file.filename.split(".")[-1].lower() if file.filename and "." in file.filename else "jpg" # 上传到MinIO - avatar_url = upload_image_to_minio(file_content, file_extension) + avatar_url = upload_image_to_minio("avatar", file_content, file_extension) # 更新用户头像 current_user.avatar = avatar_url diff --git a/src/agents/chatbot/tools.py b/src/agents/chatbot/tools.py index af9b8f0f..ee83c8b0 100644 --- a/src/agents/chatbot/tools.py +++ b/src/agents/chatbot/tools.py @@ -60,7 +60,7 @@ async def text_to_img_qwen(text: str) -> str: response = requests.get(image_url) file_data = response.content - image_url = upload_image_to_minio(data=file_data, file_extension="jpg") + image_url = upload_image_to_minio(bucket_name="generated-images", data=file_data, file_extension="jpg") logger.info(f"Image uploaded. URL: {image_url}") return image_url diff --git a/src/storage/minio/client.py b/src/storage/minio/client.py index 9e2a4087..432efd84 100644 --- a/src/storage/minio/client.py +++ b/src/storage/minio/client.py @@ -3,6 +3,7 @@ MinIO 存储客户端 简化的 MinIO 对象存储操作 """ +import json import os import uuid from io import BytesIO @@ -32,6 +33,8 @@ class MinIOClient: 简化的 MinIO 客户端类 """ + PUBLIC_READ_BUCKETS = {"generated-images", "avatar"} + def __init__(self): """初始化 MinIO 客户端""" self.endpoint = os.getenv("MINIO_URI", "http://milvus-minio:9000") @@ -41,7 +44,12 @@ class MinIOClient: # 设置公开访问端点 if os.getenv("RUNNING_IN_DOCKER"): - host_ip = os.getenv("HOST_IP", "localhost") + host_ip = (os.getenv("HOST_IP") or "").strip() + if not host_ip: + host_ip = "localhost" + if "://" in host_ip: + host_ip = host_ip.split("://")[-1] + host_ip = host_ip.rstrip("/") self.public_endpoint = f"{host_ip}:9000" else: self.public_endpoint = "localhost:9000" @@ -62,14 +70,23 @@ class MinIOClient: def ensure_bucket_exists(self, bucket_name: str) -> bool: """确保存储桶存在""" try: + created = False if not self.client.bucket_exists(bucket_name): self.client.make_bucket(bucket_name) + created = True logger.info(f"存储桶 '{bucket_name}' 已创建") - return True + + self._ensure_public_read_access(bucket_name) + + if created and bucket_name in self.PUBLIC_READ_BUCKETS: + logger.info(f"存储桶 '{bucket_name}' 已配置为公开可读") + return True except S3Error as e: logger.error(f"存储桶 '{bucket_name}' 错误: {e}") raise StorageError(f"Error with bucket '{bucket_name}': {e}") + except StorageError: + raise def upload_file( self, bucket_name: str, object_name: str, data: bytes, content_type: str = "application/octet-stream" @@ -167,6 +184,35 @@ class MinIOClient: return False raise StorageError(f"检查文件存在性失败: {e}") + def _ensure_public_read_access(self, bucket_name: str) -> None: + """设置存储桶策略,允许公开读取对象""" + if bucket_name not in self.PUBLIC_READ_BUCKETS: + return + + policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"AWS": ["*"]}, + "Action": ["s3:GetObject"], + "Resource": [f"arn:aws:s3:::{bucket_name}/*"], + }, + { + "Effect": "Allow", + "Principal": {"AWS": ["*"]}, + "Action": ["s3:ListBucket"], + "Resource": [f"arn:aws:s3:::{bucket_name}"], + }, + ], + } + + try: + self.client.set_bucket_policy(bucket_name, json.dumps(policy)) + except S3Error as e: + logger.warning(f"设置存储桶 '{bucket_name}' 公共读取策略失败: {e}") + raise StorageError(f"无法设置存储桶公共访问策略: {e}") + # 全局客户端实例 _default_client = None @@ -180,7 +226,7 @@ def get_minio_client() -> MinIOClient: return _default_client -def upload_image_to_minio(data: bytes, file_extension: str = "jpg") -> str: +def upload_image_to_minio(bucket_name: str, data: bytes, file_extension: str = "jpg") -> str: """ 上传图片到 MinIO(保持向后兼容) @@ -194,6 +240,6 @@ def upload_image_to_minio(data: bytes, file_extension: str = "jpg") -> str: client = get_minio_client() file_name = f"{uuid.uuid4()}.{file_extension}" result = client.upload_file( - bucket_name="generated-images", object_name=file_name, data=data, content_type=f"image/{file_extension}" + bucket_name=bucket_name, object_name=file_name, data=data, content_type=f"image/{file_extension}" ) return result.url