#!/usr/bin/env python3 # -*- coding: utf-8 -*- import logging import os import shutil from pathlib import Path from typing import List, Optional import hashlib logger = logging.getLogger(__name__) class FileUtils: """文件操作工具类""" @staticmethod def ensure_directory(directory_path: str) -> bool: """确保目录存在""" try: path = Path(directory_path) path.mkdir(parents=True, exist_ok=True) logger.debug(f"目录已确保存在: {directory_path}") return True except Exception as e: logger.error(f"创建目录时出错 {directory_path}: {str(e)}", exc_info=True) return False @staticmethod def safe_write(file_path: str, content: str, backup: bool = True) -> bool: """安全写入文件(支持备份)""" try: path = Path(file_path) # 备份原文件 if backup and path.exists(): backup_path = path.with_suffix(path.suffix + '.bak') shutil.copy2(path, backup_path) logger.debug(f"文件已备份: {backup_path}") # 写入新内容 with open(path, 'w', encoding='utf-8') as f: f.write(content) logger.debug(f"文件写入成功: {file_path}, 大小: {len(content)} 字节") return True except Exception as e: logger.error(f"写入文件时出错 {file_path}: {str(e)}", exc_info=True) return False @staticmethod def safe_read(file_path: str, default: str = "") -> str: """安全读取文件""" try: path = Path(file_path) if not path.exists(): logger.warning(f"文件不存在: {file_path}") return default with open(path, 'r', encoding='utf-8') as f: content = f.read() logger.debug(f"文件读取成功: {file_path}, 大小: {len(content)} 字节") return content except Exception as e: logger.error(f"读取文件时出错 {file_path}: {str(e)}", exc_info=True) return default @staticmethod def list_files(directory: str, pattern: str = "*", recursive: bool = False) -> List[Path]: """列出目录中的文件""" try: path = Path(directory) if not path.exists(): logger.warning(f"目录不存在: {directory}") return [] if recursive: files = list(path.rglob(pattern)) else: files = list(path.glob(pattern)) # 过滤出文件(非目录) files = [f for f in files if f.is_file()] logger.debug(f"列出文件: {directory}, 模式: {pattern}, 找到 {len(files)} 个文件") return files except Exception as e: logger.error(f"列出文件时出错 {directory}: {str(e)}", exc_info=True) return [] @staticmethod def calculate_file_hash(file_path: str, algorithm: str = "md5") -> Optional[str]: """计算文件哈希值""" try: path = Path(file_path) if not path.exists(): logger.warning(f"文件不存在: {file_path}") return None hash_func = getattr(hashlib, algorithm)() with open(path, 'rb') as f: for chunk in iter(lambda: f.read(4096), b""): hash_func.update(chunk) file_hash = hash_func.hexdigest() logger.debug(f"文件哈希计算完成: {file_path} -> {algorithm}:{file_hash}") return file_hash except Exception as e: logger.error(f"计算文件哈希时出错 {file_path}: {str(e)}", exc_info=True) return None @staticmethod def cleanup_old_files(directory: str, pattern: str, keep_count: int) -> int: """清理旧文件,保留指定数量的最新文件""" try: files = FileUtils.list_files(directory, pattern) if len(files) <= keep_count: logger.debug(f"文件数量未超过限制,无需清理: {directory}") return 0 # 按修改时间排序 files.sort(key=lambda x: x.stat().st_mtime, reverse=True) # 删除旧文件 removed_count = 0 for file_to_remove in files[keep_count:]: try: file_to_remove.unlink() removed_count += 1 logger.debug(f"删除旧文件: {file_to_remove}") except Exception as e: logger.error(f"删除文件时出错 {file_to_remove}: {str(e)}", exc_info=True) logger.info(f"文件清理完成: {directory}, 删除 {removed_count} 个文件") return removed_count except Exception as e: logger.error(f"清理旧文件时出错 {directory}: {str(e)}", exc_info=True) return 0