| import os |
| import asyncio |
| import logging |
| import time |
| import aiofiles |
| from datetime import datetime |
| from pathlib import Path |
| from typing import Dict, List, Optional |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| class FileCachePlugin: |
| """文件缓存插件""" |
|
|
| def __init__(self): |
| self.name = "cache" |
| self.enabled = False |
| self.cache_dir = None |
| self.temp_dir = None |
| self.permanent_dir = None |
| self.file_metadata = {} |
|
|
| def on_enable(self): |
| """插件启用时调用""" |
| self.enabled = True |
| self._init_directories() |
| self._load_metadata() |
| logger.info(f"{self.name} 已启用") |
|
|
| def on_disable(self): |
| """插件禁用时调用""" |
| self.enabled = False |
| try: |
| from . import api |
|
|
| api.stop_cleanup_task() |
| except Exception as e: |
| logger.error(f"停止清理任务失败: {e}") |
| logger.info(f"{self.name} 已禁用") |
|
|
| def _init_directories(self): |
| """初始化缓存目录""" |
| plugin_dir = Path(__file__).parent / "cache" |
| plugin_dir.mkdir(exist_ok=True) |
|
|
| self.cache_dir = plugin_dir |
| self.temp_dir = plugin_dir / "temp" |
| self.permanent_dir = plugin_dir / "permanent" |
|
|
| self.temp_dir.mkdir(exist_ok=True) |
| self.permanent_dir.mkdir(exist_ok=True) |
|
|
| def _load_metadata(self): |
| """从缓存目录加载文件元数据""" |
| self.file_metadata = {} |
|
|
| |
| if self.temp_dir.exists(): |
| for file_path in self.temp_dir.iterdir(): |
| if file_path.is_file(): |
| file_id = file_path.stem |
| stat = file_path.stat() |
| self.file_metadata[file_id] = { |
| "name": file_path.name, |
| "type": "temp", |
| "created": stat.st_mtime, |
| "path": str(file_path), |
| } |
|
|
| |
| if self.permanent_dir.exists(): |
| for file_path in self.permanent_dir.iterdir(): |
| if file_path.is_file(): |
| file_id = file_path.stem |
| stat = file_path.stat() |
| self.file_metadata[file_id] = { |
| "name": file_path.name, |
| "type": "permanent", |
| "created": stat.st_mtime, |
| "path": str(file_path), |
| } |
|
|
| async def cleanup_expired_files(self): |
| """清理超过8小时的临时文件""" |
| current_time = time.time() |
| expired_files = [] |
|
|
| for file_id, metadata in list(self.file_metadata.items()): |
| if metadata["type"] == "temp": |
| file_age = current_time - metadata["created"] |
| |
| if file_age > 28800: |
| expired_files.append(file_id) |
|
|
| for file_id in expired_files: |
| await self.delete_file(file_id) |
|
|
| async def save_file( |
| self, |
| file_id: str, |
| file_content: bytes, |
| original_filename: str, |
| is_temp: bool = True, |
| ) -> bool: |
| """保存文件到缓存目录""" |
| try: |
| target_dir = self.temp_dir if is_temp else self.permanent_dir |
| file_path = target_dir / original_filename |
|
|
| |
| if file_path.exists(): |
| name, suffix = ( |
| original_filename.rsplit(".", 1) |
| if "." in original_filename |
| else (original_filename, "") |
| ) |
| timestamp = int(time.time() * 1000) |
| original_filename = ( |
| f"{name}_{timestamp}.{suffix}" if suffix else f"{name}_{timestamp}" |
| ) |
| file_path = target_dir / original_filename |
|
|
| |
| async with aiofiles.open(file_path, "wb") as f: |
| await f.write(file_content) |
|
|
| |
| self.file_metadata[file_id] = { |
| "name": original_filename, |
| "type": "temp" if is_temp else "permanent", |
| "created": time.time(), |
| "path": str(file_path), |
| } |
|
|
| return True |
| except Exception as e: |
| logger.error(f"保存文件失败: {e}") |
| return False |
|
|
| async def delete_file(self, file_id: str) -> bool: |
| """删除文件""" |
| try: |
| if file_id not in self.file_metadata: |
| return False |
|
|
| file_path = Path(self.file_metadata[file_id]["path"]) |
| if file_path.exists(): |
| file_path.unlink() |
|
|
| del self.file_metadata[file_id] |
| return True |
| except Exception as e: |
| logger.error(f"删除文件失败: {e}") |
| return False |
|
|
| def get_file_list(self) -> List[Dict]: |
| """获取文件列表""" |
| file_list = [] |
| for file_id, metadata in self.file_metadata.items(): |
| file_list.append( |
| { |
| "file_id": file_id, |
| "name": metadata["name"], |
| "type": metadata["type"], |
| "created": datetime.fromtimestamp(metadata["created"]).isoformat(), |
| "url": f"/plugins/cache/api/file/{file_id}", |
| } |
| ) |
| return file_list |
|
|
| def get_file_path(self, file_id: str) -> Optional[str]: |
| """获取文件路径""" |
| if file_id in self.file_metadata: |
| return self.file_metadata[file_id]["path"] |
| return None |
|
|
| def get_status(self) -> Dict: |
| """获取插件状态""" |
| return { |
| "name": self.name, |
| "enabled": self.enabled, |
| "file_count": len(self.file_metadata), |
| "cache_dir": str(self.cache_dir), |
| } |
|
|
|
|
| |
| plugin = FileCachePlugin() |
|
|