first commit
This commit is contained in:
184
backend/app/utils/file.py
Normal file
184
backend/app/utils/file.py
Normal file
@@ -0,0 +1,184 @@
|
||||
from minio import Minio
|
||||
from minio.error import S3Error
|
||||
from typing import Optional, Tuple
|
||||
import io
|
||||
import os
|
||||
import logging
|
||||
|
||||
from app.config.settings import settings
|
||||
|
||||
|
||||
class MinioClient:
|
||||
"""MinIO客户端类"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化MinIO客户端"""
|
||||
try:
|
||||
self.client = Minio(
|
||||
settings.MINIO_ENDPOINT,
|
||||
access_key=settings.MINIO_ACCESS_KEY,
|
||||
secret_key=settings.MINIO_SECRET_KEY,
|
||||
secure=settings.MINIO_SECURE
|
||||
)
|
||||
self.bucket_name = settings.MINIO_BUCKET_NAME
|
||||
self.is_connected = True # 先设置为True,这样在调用其他方法时不会报错
|
||||
|
||||
# 确保存储桶存在
|
||||
self._ensure_bucket_exists()
|
||||
except Exception as e:
|
||||
logging.warning(f"Failed to connect to MinIO: {e}. Running in offline mode.")
|
||||
self.client = None
|
||||
self.bucket_name = settings.MINIO_BUCKET_NAME
|
||||
self.is_connected = False
|
||||
|
||||
def _ensure_bucket_exists(self):
|
||||
"""确保存储桶存在"""
|
||||
if not self.is_connected:
|
||||
return
|
||||
|
||||
try:
|
||||
if not self.client.bucket_exists(self.bucket_name):
|
||||
self.client.make_bucket(self.bucket_name)
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO bucket error: {e}")
|
||||
|
||||
def upload_file(self, file_path: str, object_name: str) -> bool:
|
||||
"""上传文件"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. Upload skipped.")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.client.fput_object(
|
||||
self.bucket_name,
|
||||
object_name,
|
||||
file_path
|
||||
)
|
||||
return True
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO upload error: {e}")
|
||||
return False
|
||||
|
||||
def upload_fileobj(self, file_obj: io.BytesIO, object_name: str, content_type: str = "application/octet-stream") -> bool:
|
||||
"""上传文件对象"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. Upload skipped.")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.client.put_object(
|
||||
self.bucket_name,
|
||||
object_name,
|
||||
file_obj,
|
||||
length=-1,
|
||||
part_size=10*1024*1024,
|
||||
content_type=content_type
|
||||
)
|
||||
return True
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO upload error: {e}")
|
||||
return False
|
||||
|
||||
def download_file(self, object_name: str, file_path: str) -> bool:
|
||||
"""下载文件"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. Download skipped.")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.client.fget_object(
|
||||
self.bucket_name,
|
||||
object_name,
|
||||
file_path
|
||||
)
|
||||
return True
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO download error: {e}")
|
||||
return False
|
||||
|
||||
def get_object(self, object_name: str) -> Optional[bytes]:
|
||||
"""获取对象内容"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. Get object skipped.")
|
||||
return None
|
||||
|
||||
try:
|
||||
response = self.client.get_object(
|
||||
self.bucket_name,
|
||||
object_name
|
||||
)
|
||||
data = response.read()
|
||||
response.close()
|
||||
response.release_conn()
|
||||
return data
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO get object error: {e}")
|
||||
return None
|
||||
|
||||
def delete_object(self, object_name: str) -> bool:
|
||||
"""删除对象"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. Delete object skipped.")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.client.remove_object(
|
||||
self.bucket_name,
|
||||
object_name
|
||||
)
|
||||
return True
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO delete error: {e}")
|
||||
return False
|
||||
|
||||
def list_objects(self, prefix: str = "") -> list:
|
||||
"""列出对象"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. List objects skipped.")
|
||||
return []
|
||||
|
||||
try:
|
||||
objects = []
|
||||
for obj in self.client.list_objects(
|
||||
self.bucket_name,
|
||||
prefix=prefix,
|
||||
recursive=True
|
||||
):
|
||||
objects.append(obj.object_name)
|
||||
return objects
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO list objects error: {e}")
|
||||
return []
|
||||
|
||||
def get_presigned_url(self, object_name: str, expires: int = 604800) -> Optional[str]:
|
||||
"""获取预签名URL"""
|
||||
if not self.is_connected:
|
||||
logging.warning("MinIO is not connected. Get presigned URL skipped.")
|
||||
return None
|
||||
|
||||
try:
|
||||
url = self.client.presigned_get_object(
|
||||
self.bucket_name,
|
||||
object_name,
|
||||
expires=expires
|
||||
)
|
||||
return url
|
||||
except S3Error as e:
|
||||
logging.warning(f"MinIO presigned url error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# 创建全局文件存储实例
|
||||
try:
|
||||
file_storage = MinioClient()
|
||||
except Exception as e:
|
||||
# 如果初始化失败,创建一个模拟实例
|
||||
class MockFileStorage:
|
||||
def __getattr__(self, name):
|
||||
def mock_method(*args, **kwargs):
|
||||
logging.warning(f"MinIO is not available. Method '{name}' will not execute.")
|
||||
return None if name.startswith('get_') or name == 'list_objects' else False
|
||||
return mock_method
|
||||
|
||||
file_storage = MockFileStorage()
|
||||
logging.warning(f"Failed to initialize MinIO client: {e}. Using mock instance.")
|
||||
Reference in New Issue
Block a user