from __future__ import annotations import os from typing import BinaryIO, Dict, Optional from .interface import StorageInterface class LocalStorage(StorageInterface): """本地文件存储适配器""" storage_type = "local" def __init__(self, config: dict): self.base_path = config.get("base_path") if not self.base_path: raise ValueError("LocalStorage 需要 base_path 配置") os.makedirs(self.base_path, exist_ok=True) def _abs_path(self, key: str) -> str: """获取文件绝对路径""" return os.path.join(self.base_path, key) def upload(self, file_stream: BinaryIO, key: str, mime_type: Optional[str] = None) -> Dict: """上传文件到本地""" # 本地文件存储需要移除前缀 key = key.lstrip(self.storage_type + ":") abs_path = self._abs_path(key) os.makedirs(os.path.dirname(abs_path), exist_ok=True) with open(abs_path, "wb") as f: f.write(file_stream.read()) file_size = os.path.getsize(abs_path) return { "path": abs_path, "size": file_size, } def append_chunk(self, key: str, chunk_stream: BinaryIO, offset: int) -> None: """追加写入数据块""" abs_path = self._abs_path(key) os.makedirs(os.path.dirname(abs_path), exist_ok=True) mode = "r+b" if os.path.exists(abs_path) else "wb" with open(abs_path, mode) as f: f.seek(offset) f.write(chunk_stream.read()) def download(self, key: str) -> BinaryIO: """下载文件""" abs_path = self._abs_path(key) if not os.path.exists(abs_path): raise FileNotFoundError(f"文件不存在: {key}") return open(abs_path, "rb") def delete(self, key: str) -> None: """删除文件""" abs_path = self._abs_path(key) if os.path.exists(abs_path): os.remove(abs_path) def exists(self, key: str) -> bool: """检查文件是否存在""" abs_path = self._abs_path(key) return os.path.exists(abs_path) def get_url(self, key: str, expires: int = 3600) -> str: """获取文件访问URL(本地存储返回下载路由)""" # 本地存储通过后端路由提供下载,无需签名 return key