""" MinIO SDK 对象存储封装,支持配置注入 """ import os import time import random import string from typing import Optional try: from minio import Minio MINIO_AVAILABLE = True except ImportError: MINIO_AVAILABLE = False Minio = None class MinioSDK: """MinIO 对象存储 SDK""" def __init__( self, end_point: str, access_key: str, secret_key: str, bucket: str = "hackrobot", use_ssl: bool = True, port: Optional[int] = None, region: str = "china", public_url: Optional[str] = None, upload_prefix: str = "joins", ): if not MINIO_AVAILABLE: raise ImportError("minio 未安装,请执行: pip install minio") self.end_point = end_point self.port = port or (443 if use_ssl else 80) self.access_key = access_key self.secret_key = secret_key self.bucket = bucket self.use_ssl = use_ssl self.region = region self.public_url = public_url or f"https://{end_point}/{bucket}" self.upload_prefix = upload_prefix.rstrip("/") self._client: Optional[Minio] = None def get_client(self) -> "Minio": """获取 MinIO 客户端""" if self._client is None: endpoint = f"{self.end_point}:{self.port}" if self.port not in (80, 443) else self.end_point self._client = Minio( endpoint, access_key=self.access_key, secret_key=self.secret_key, secure=self.use_ssl, region=self.region, ) return self._client def generate_object_key(self, ext: str) -> str: """生成上传用的对象键""" ext = ext.lstrip(".") suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=8)) return f"{self.upload_prefix}/{int(time.time() * 1000)}_{suffix}.{ext}" def upload_buffer( self, data: bytes, object_key: str, content_type: str = "application/octet-stream", ) -> dict: """ 上传文件到 MinIO :return: {"url": str, "object_key": str} """ client = self.get_client() from io import BytesIO stream = BytesIO(data) client.put_object( self.bucket, object_key, stream, len(data), content_type=content_type, ) url = f"{self.public_url.rstrip('/')}/{object_key}" return {"url": url, "object_key": object_key} @property def enabled(self) -> bool: """是否启用(需配置 access_key/secret_key)""" return bool(self.end_point and self.access_key and self.secret_key)