| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- from minio import Minio
- from minio.error import S3Error
- from app.core.config import settings
- import io
- import uuid
- from datetime import datetime
- import logging
- logger = logging.getLogger(__name__)
- class MessageStorage:
- def __init__(self):
- try:
- # Handle endpoint protocol
- endpoint = settings.MINIO_ENDPOINT
- secure = settings.MINIO_SECURE
-
- if endpoint.startswith("http://"):
- endpoint = endpoint.replace("http://", "")
- secure = False
- elif endpoint.startswith("https://"):
- endpoint = endpoint.replace("https://", "")
- secure = True
-
- self.client = Minio(
- endpoint=endpoint,
- access_key=settings.MINIO_ACCESS_KEY,
- secret_key=settings.MINIO_SECRET_KEY,
- secure=secure
- )
- self.bucket_name = settings.MINIO_BUCKET_NAME
- self._ensure_bucket()
- except Exception as e:
- logger.error(f"MinIO init failed: {e}")
- self.client = None
- def _ensure_bucket(self):
- if not self.client: return
- try:
- if not self.client.bucket_exists(self.bucket_name):
- self.client.make_bucket(self.bucket_name)
- # 移除公开读策略,默认为私有
- # self.client.set_bucket_policy(self.bucket_name, json.dumps(policy))
- except S3Error as e:
- logger.error(f"MinIO bucket error: {e}")
- def get_presigned_url(self, object_name: str, expires=None):
- """生成预签名访问链接"""
- if not self.client: return None
- try:
- from datetime import timedelta
- if expires is None:
- expires = timedelta(hours=1)
-
- return self.client.get_presigned_url(
- "GET",
- self.bucket_name,
- object_name,
- expires=expires
- )
- except Exception as e:
- logger.error(f"Generate presigned url failed: {e}")
- return None
- def upload_message_file(self, file_data: bytes, filename: str, content_type: str, user_id: int) -> str:
- """
- 上传消息附件
- 路径格式: messages/{user_id}/{year}/{month}/{uuid}.ext
- 返回: object_name (用于存储和生成签名URL)
- """
- if not self.client:
- raise Exception("Storage service unavailable")
- # 生成结构化路径
- ext = filename.split('.')[-1] if '.' in filename else 'bin'
- now = datetime.now()
- object_name = f"messages/{user_id}/{now.year}/{now.month:02d}/{uuid.uuid4()}.{ext}"
-
- try:
- self.client.put_object(
- bucket_name=self.bucket_name,
- object_name=object_name,
- data=io.BytesIO(file_data),
- length=len(file_data),
- content_type=content_type
- )
-
- return object_name
-
- except S3Error as e:
- logger.error(f"Upload failed: {e}")
- raise Exception("File upload failed")
- # 单例实例
- minio_storage = MessageStorage()
|