import uuid from typing import Optional, BinaryIO from botocore.exceptions import ClientError import boto3 from app.core.config import settings class StorageService: def __init__(self): self.client = None self._initialize_client() def _initialize_client(self): """Инициализация S3/MinIO клиента""" self.client = boto3.client( "s3", endpoint_url=f"{'https' if settings.STORAGE_USE_SSL else 'http'}://{settings.STORAGE_ENDPOINT}", aws_access_key_id=settings.STORAGE_ACCESS_KEY, aws_secret_access_key=settings.STORAGE_SECRET_KEY, region_name=settings.STORAGE_REGION, use_ssl=settings.STORAGE_USE_SSL, verify=False # Для MinIO в dev ) async def upload_file( self, file_obj: BinaryIO, filename: str, content_type: str = "image/jpeg" ) -> str: """Загрузить файл в хранилище""" file_key = f"{uuid.uuid4()}_{filename}" try: self.client.upload_fileobj( file_obj, settings.STORAGE_BUCKET, file_key, ExtraArgs={"ContentType": content_type} ) # Формируем URL url = f"{'https' if settings.STORAGE_USE_SSL else 'http'}://{settings.STORAGE_ENDPOINT}/{settings.STORAGE_BUCKET}/{file_key}" return url except ClientError as e: raise Exception(f"Failed to upload file: {str(e)}") async def delete_file(self, file_key: str) -> bool: """Удалить файл из хранилища""" try: # Извлекаем ключ из URL если передан полный URL if "/" in file_key: file_key = file_key.split("/")[-1] self.client.delete_object( Bucket=settings.STORAGE_BUCKET, Key=file_key ) return True except ClientError: return False async def get_file_url(self, file_key: str) -> Optional[str]: """Получить URL файла""" try: if "/" in file_key: file_key = file_key.split("/")[-1] url = self.client.generate_presigned_url( "get_object", Params={"Bucket": settings.STORAGE_BUCKET, "Key": file_key}, ExpiresIn=3600 ) return url except ClientError: return None storage_service = StorageService()