128 lines
3.3 KiB
Python
128 lines
3.3 KiB
Python
"""MinIO client wrapper for object storage operations."""
|
|
|
|
import io
|
|
import logging
|
|
from typing import Optional
|
|
|
|
from minio import Minio
|
|
from minio.error import S3Error
|
|
|
|
from config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
BUCKET_NAME = "seg-media"
|
|
|
|
_minio_client: Optional[Minio] = None
|
|
|
|
|
|
def get_minio_client() -> Minio:
|
|
"""Return a singleton MinIO client instance."""
|
|
global _minio_client
|
|
if _minio_client is None:
|
|
_minio_client = Minio(
|
|
settings.minio_endpoint,
|
|
access_key=settings.minio_access_key,
|
|
secret_key=settings.minio_secret_key,
|
|
secure=settings.minio_secure,
|
|
)
|
|
return _minio_client
|
|
|
|
|
|
def ensure_bucket_exists() -> None:
|
|
"""Create the bucket if it does not already exist."""
|
|
client = get_minio_client()
|
|
try:
|
|
if not client.bucket_exists(BUCKET_NAME):
|
|
client.make_bucket(BUCKET_NAME)
|
|
logger.info("Created MinIO bucket: %s", BUCKET_NAME)
|
|
else:
|
|
logger.info("MinIO bucket %s already exists", BUCKET_NAME)
|
|
except S3Error as exc:
|
|
logger.error("MinIO bucket check/creation failed: %s", exc)
|
|
raise
|
|
|
|
|
|
def upload_file(
|
|
object_name: str,
|
|
data: bytes,
|
|
content_type: str = "application/octet-stream",
|
|
length: int = -1,
|
|
) -> str:
|
|
"""Upload bytes to MinIO and return the object name.
|
|
|
|
Args:
|
|
object_name: Destination path inside the bucket.
|
|
data: Raw bytes or a file-like object.
|
|
content_type: MIME type of the object.
|
|
length: Object size; -1 for unknown (uses chunked upload).
|
|
|
|
Returns:
|
|
The object name (same as input).
|
|
"""
|
|
client = get_minio_client()
|
|
if isinstance(data, bytes):
|
|
data = io.BytesIO(data)
|
|
length = len(data.getvalue())
|
|
|
|
try:
|
|
client.put_object(
|
|
BUCKET_NAME,
|
|
object_name,
|
|
data,
|
|
length=length,
|
|
content_type=content_type,
|
|
)
|
|
logger.info("Uploaded to MinIO: %s", object_name)
|
|
return object_name
|
|
except S3Error as exc:
|
|
logger.error("MinIO upload failed: %s", exc)
|
|
raise
|
|
|
|
|
|
from datetime import timedelta
|
|
|
|
def get_presigned_url(
|
|
object_name: str,
|
|
expires: int = 3600,
|
|
method: str = "GET",
|
|
) -> str:
|
|
"""Generate a presigned URL for an object.
|
|
|
|
Args:
|
|
object_name: Path inside the bucket.
|
|
expires: Expiration time in seconds (default 1 hour).
|
|
method: HTTP method (GET or PUT).
|
|
|
|
Returns:
|
|
Presigned URL string.
|
|
"""
|
|
client = get_minio_client()
|
|
try:
|
|
url = client.get_presigned_url(method, BUCKET_NAME, object_name, expires=timedelta(seconds=expires))
|
|
return url
|
|
except S3Error as exc:
|
|
logger.error("MinIO presigned URL failed: %s", exc)
|
|
raise
|
|
|
|
|
|
def download_file(object_name: str) -> bytes:
|
|
"""Download an object from MinIO and return its bytes.
|
|
|
|
Args:
|
|
object_name: Path inside the bucket.
|
|
|
|
Returns:
|
|
Raw bytes of the object.
|
|
"""
|
|
client = get_minio_client()
|
|
try:
|
|
response = client.get_object(BUCKET_NAME, object_name)
|
|
data = response.read()
|
|
response.close()
|
|
response.release_conn()
|
|
return data
|
|
except S3Error as exc:
|
|
logger.error("MinIO download failed: %s", exc)
|
|
raise
|