Files
ai-doc-template-system/backend/app/services/storage.py
T
2026-07-01 20:27:15 +08:00

102 lines
3.1 KiB
Python

import os
from io import BytesIO
from typing import Any
STORAGE_BASE = os.getenv("LOCAL_STORAGE_PATH", "./data/files")
MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT", "localhost:9000")
MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY", "minioadmin")
MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY", "minioadmin")
MINIO_SECURE = os.getenv("MINIO_SECURE", "false").lower() == "true"
def _local_path(bucket: str, file_path: str) -> str:
return os.path.join(STORAGE_BASE, bucket, file_path)
def _save_local(bucket: str, file_path: str, content: bytes) -> str:
full_path = _local_path(bucket, file_path)
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(full_path, "wb") as f:
f.write(content)
return full_path
def _read_local(bucket: str, file_path: str) -> bytes:
with open(_local_path(bucket, file_path), "rb") as f:
return f.read()
def get_minio_client() -> Any:
from minio import Minio
return Minio(
MINIO_ENDPOINT,
access_key=MINIO_ACCESS_KEY,
secret_key=MINIO_SECRET_KEY,
secure=MINIO_SECURE,
)
def ensure_bucket(bucket_name: str) -> bool:
"""确保 bucket 存在;MinIO 不可用时返回 False 表示使用本地降级。"""
try:
client = get_minio_client()
if not client.bucket_exists(bucket_name):
client.make_bucket(bucket_name)
return True
except Exception:
return False
def upload_file(bucket: str, file_path: str, content: bytes) -> str:
"""上传文件到 MinIO;连接失败时降级保存到本地文件系统。"""
if ensure_bucket(bucket):
try:
client = get_minio_client()
client.put_object(
bucket,
file_path,
BytesIO(content),
length=len(content),
content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
return f"minio://{bucket}/{file_path}"
except Exception:
pass
return _save_local(bucket, file_path, content)
def download_file(bucket: str, file_path: str) -> bytes:
"""从 MinIO 下载文件;读取失败时尝试本地降级路径。"""
if ensure_bucket(bucket):
try:
client = get_minio_client()
response = client.get_object(bucket, file_path)
try:
return response.read()
finally:
response.close()
response.release_conn()
except Exception:
pass
return _read_local(bucket, file_path)
def save_file(content: bytes, file_path: str) -> str:
"""兼容旧调用:保存到默认 templates bucket。"""
return upload_file("templates", file_path, content)
def read_file(file_path: str) -> bytes:
"""兼容旧调用:从默认 templates bucket 读取。"""
return download_file("templates", file_path)
def delete_file(file_path: str):
"""删除本地降级文件。MinIO 对象删除后续按接口需要再补充。"""
full_path = _local_path("templates", file_path)
if os.path.exists(full_path):
os.remove(full_path)