Files
server-core/app/infrastructure/oss.py
T

64 lines
2.2 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""基础设施层 · OSS 对象存储异步封装(aioboto3,兼容阿里云 OSS / MinIO)。
未配置 OSS 时降级为本地上传目录(uploads/),保证无 OSS 也可用。
"""
from __future__ import annotations
import os
from pathlib import Path
from typing import Optional
from .. import config
class OSS:
"""对象存储:上传文件 / 生成访问 URL。配置了 OSS_ENDPOINT 才启用,否则落本地上传目录。"""
def __init__(self) -> None:
self._client: Optional[object] = None
self.enabled = bool(config.OSS_ENDPOINT)
self.bucket = config.OSS_BUCKET
self.local_dir = Path(__file__).resolve().parent.parent.parent / "uploads"
self.local_dir.mkdir(parents=True, exist_ok=True)
async def _get_client(self):
if self._client is None:
import aioboto3
session = aioboto3.Session(
aws_access_key_id=config.OSS_ACCESS_KEY,
aws_secret_access_key=config.OSS_SECRET_KEY,
)
self._client = session.client(
"s3",
endpoint_url=config.OSS_ENDPOINT,
region_name="oss-cn-hangzhou",
)
return self._client
async def upload(self, key: str, data: bytes, content_type: str = "application/octet-stream") -> str:
"""上传对象,返回可访问的相对 URL。"""
if self.enabled:
client = await self._get_client()
await client.put_object(Bucket=self.bucket, Key=key, Body=data, ContentType=content_type)
return f"/oss/{key}"
# 本地降级
dest = self.local_dir / key
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_bytes(data)
return f"/uploads/{key}"
async def presigned_url(self, key: str, expires: int = 3600) -> str:
"""生成临时访问 URL(OSS 启用时)。"""
if self.enabled:
client = await self._get_client()
return await client.generate_presigned_url(
"get_object", Params={"Bucket": self.bucket, "Key": key}, ExpiresIn=expires
)
return f"/uploads/{key}"
@staticmethod
def is_configured() -> bool:
return bool(config.OSS_ENDPOINT)