import 'dart:io'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; /// 文件工具类 /// 提供文件操作、目录管理和代码格式化功能 class FileUtils { static final Logger _logger = Logger('FileUtils'); /// 解析路径(支持相对路径和绝对路径) /// 如果是相对路径,相对于项目根目录(配置文件所在目录) static String resolvePath(String filePath) { // 如果是绝对路径,直接返回 if (path.isAbsolute(filePath)) { return filePath; } // 相对路径:相对于当前工作目录 // 查找配置文件所在的目录作为项目根目录 final configFile = _findConfigFile(); if (configFile != null) { final configDir = path.dirname(configFile); return path.join(configDir, filePath); } // 如果找不到配置文件,使用当前工作目录 return path.join(Directory.current.path, filePath); } /// 查找配置文件 static String? _findConfigFile() { var currentDir = Directory.current; const maxDepth = 10; var depth = 0; while (depth < maxDepth) { final configFile = File(path.join(currentDir.path, 'generator_config.yaml')); if (configFile.existsSync()) { return configFile.path; } final parent = currentDir.parent; if (parent.path == currentDir.path) { break; } currentDir = parent; depth++; } return null; } /// 确保目录存在 static Future ensureDirectoryExists(String dirPath) async { final resolvedPath = resolvePath(dirPath); final directory = Directory(resolvedPath); if (!directory.existsSync()) { await directory.create(recursive: true); } return directory; } /// 安全写入文件 static Future safeWriteFile(String filePath, String content) async { try { final resolvedPath = resolvePath(filePath); final file = File(resolvedPath); final directory = file.parent; // 确保目录存在 if (!directory.existsSync()) { await directory.create(recursive: true); } // 写入文件 await file.writeAsString(content); } on FileSystemException { rethrow; } on Object { throw FileSystemException('写入文件失败: $filePath', filePath); } } /// 安全读取文件 static Future safeReadFile(String filePath) async { try { final file = File(filePath); if (!file.existsSync()) { throw FileSystemException('文件不存在: $filePath', filePath); } return await file.readAsString(); } on FileSystemException { rethrow; } on Object { throw FileSystemException('读取文件失败: $filePath', filePath); } } /// 检查文件是否存在 static Future fileExists(String filePath) async { return File(filePath).existsSync(); } /// 检查目录是否存在 static Future directoryExists(String dirPath) async { return Directory(dirPath).existsSync(); } /// 删除文件(如果存在) static Future deleteFileIfExists(String filePath) async { final file = File(filePath); if (file.existsSync()) { await file.delete(); } } /// 删除目录(如果存在) static Future deleteDirectoryIfExists(String dirPath) async { final directory = Directory(dirPath); if (directory.existsSync()) { await directory.delete(recursive: true); } } /// 复制文件 static Future copyFile( String sourcePath, String destinationPath, ) async { try { final sourceFile = File(sourcePath); final destinationFile = File(destinationPath); if (!sourceFile.existsSync()) { throw FileSystemException('源文件不存在: $sourcePath', sourcePath); } // 确保目标目录存在 final destinationDir = destinationFile.parent; if (!destinationDir.existsSync()) { await destinationDir.create(recursive: true); } await sourceFile.copy(destinationPath); } on Object catch (e) { throw FileSystemException( '复制文件失败: $sourcePath -> $destinationPath', sourcePath, e is OSError ? e : null, ); } } /// 移动文件 static Future moveFile( String sourcePath, String destinationPath, ) async { try { final sourceFile = File(sourcePath); final destinationFile = File(destinationPath); if (!sourceFile.existsSync()) { throw FileSystemException('源文件不存在: $sourcePath', sourcePath); } // 确保目标目录存在 final destinationDir = destinationFile.parent; if (!destinationDir.existsSync()) { await destinationDir.create(recursive: true); } await sourceFile.rename(destinationPath); } on Object catch (e) { throw FileSystemException( '移动文件失败: $sourcePath -> $destinationPath', sourcePath, e is OSError ? e : null, ); } } /// 获取文件大小 static Future getFileSize(String filePath) async { try { final file = File(filePath); if (!file.existsSync()) { return 0; } return await file.length(); } on Object { return 0; } } /// 获取目录大小 static Future getDirectorySize(String dirPath) async { try { final directory = Directory(dirPath); if (!await directory.exists()) { return 0; } var totalSize = 0; for (final entity in directory.listSync(recursive: true)) { if (entity is File) { totalSize += entity.lengthSync(); } } return totalSize; } on Object { return 0; } } /// 列出目录中的文件 static Future> listFiles( String dirPath, { String? extension, }) async { try { final directory = Directory(dirPath); if (!await directory.exists()) { return []; } final files = []; for (final entity in directory.listSync()) { if (entity is File) { if (extension == null || entity.path.endsWith(extension)) { files.add(entity.path); } } } return files; } on Object { return []; } } /// 列出目录中的子目录 static Future> listDirectories(String dirPath) async { try { final directory = Directory(dirPath); if (!await directory.exists()) { return []; } final directories = []; for (final entity in directory.listSync()) { if (entity is Directory) { directories.add(entity.path); } } return directories; } on Object { return []; } } /// 创建备份文件 static Future createBackup(String filePath) async { try { final file = File(filePath); if (!file.existsSync()) { throw FileSystemException('文件不存在: $filePath', filePath); } final timestamp = DateTime.now().toIso8601String().replaceAll(':', '-'); final backupPath = '$filePath.backup.$timestamp'; await file.copy(backupPath); return backupPath; } on Object catch (e) { throw FileSystemException( '创建备份失败: $filePath', filePath, e is OSError ? e : null, ); } } /// 恢复备份文件 static Future restoreBackup( String backupPath, String originalPath, ) async { try { final backupFile = File(backupPath); if (!backupFile.existsSync()) { throw FileSystemException('备份文件不存在: $backupPath', backupPath); } await backupFile.copy(originalPath); } on Object catch (e) { throw FileSystemException( '恢复备份失败: $backupPath -> $originalPath', backupPath, e is OSError ? e : null, ); } } /// 格式化文件路径 static String formatPath(String filePath) { return path.normalize(filePath); } /// 获取文件名(不包括路径) static String getFileName(String filePath) { return path.basename(filePath); } /// 获取文件名(不包括扩展名) static String getFileNameWithoutExtension(String filePath) { return path.basenameWithoutExtension(filePath); } /// 获取文件扩展名 static String getFileExtension(String filePath) { return path.extension(filePath); } /// 获取文件所在目录 static String getDirectoryPath(String filePath) { return path.dirname(filePath); } /// 连接路径 static String joinPath(List parts) { return path.joinAll(parts); } /// 获取相对路径 static String getRelativePath(String filePath, String basePath) { return path.relative(filePath, from: basePath); } /// 获取绝对路径 static String getAbsolutePath(String filePath) { return path.absolute(filePath); } /// 检查路径是否为绝对路径 static bool isAbsolute(String filePath) { return path.isAbsolute(filePath); } /// 清理文件名(移除不合法字符) static String sanitizeFileName(String fileName) { // 移除或替换不合法的文件名字符 return fileName .replaceAll(RegExp(r'[<>:"/\\|?*]'), '_') .replaceAll(RegExp(r'\s+'), '_') .replaceAll(RegExp('_{2,}'), '_') .replaceAll(RegExp(r'^_|_$'), ''); } /// 生成唯一文件名 static Future generateUniqueFileName( String basePath, String fileName, ) async { final extension = getFileExtension(fileName); final nameWithoutExt = getFileNameWithoutExtension(fileName); var uniqueName = fileName; var counter = 1; while (File(path.join(basePath, uniqueName)).existsSync()) { uniqueName = '${nameWithoutExt}_$counter$extension'; counter++; } return uniqueName; } /// 批量操作文件 static Future batchOperation( List filePaths, Future Function(String filePath) operation, ) async { for (final filePath in filePaths) { try { await operation(filePath); } on Object catch (e) { _logger.warning('批量操作失败: $filePath - $e'); } } } /// 查找文件 static Future> findFiles( String searchPath, String pattern, { bool recursive = false, }) async { try { final directory = Directory(searchPath); if (!await directory.exists()) { return []; } final regex = RegExp(pattern); final foundFiles = []; for (final entity in directory.listSync(recursive: recursive)) { if (entity is File) { final fileName = getFileName(entity.path); if (regex.hasMatch(fileName)) { foundFiles.add(entity.path); } } } return foundFiles; } on Object { return []; } } /// 获取文件修改时间 static Future getFileModifiedTime(String filePath) async { try { final file = File(filePath); if (!file.existsSync()) { return null; } final stat = file.statSync(); return stat.modified; } on Object { return null; } } /// 比较文件修改时间 static Future isFileNewer(String filePath1, String filePath2) async { final time1 = await getFileModifiedTime(filePath1); final time2 = await getFileModifiedTime(filePath2); if (time1 == null || time2 == null) { return false; } return time1.isAfter(time2); } /// 计算文件哈希 static Future calculateFileHash(String filePath) async { try { final file = File(filePath); if (!file.existsSync()) { return null; } final bytes = await file.readAsBytes(); return bytes.hashCode.toString(); } on Object { return null; } } /// 格式化文件大小 static String formatFileSize(int bytes) { if (bytes < 1024) { return '${bytes}B'; } else if (bytes < 1024 * 1024) { return '${(bytes / 1024).toStringAsFixed(1)}KB'; } else if (bytes < 1024 * 1024 * 1024) { return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB'; } else { return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB'; } } /// 创建临时文件 static Future createTempFile(String prefix, {String? suffix}) async { final tempDir = Directory.systemTemp; final timestamp = DateTime.now().millisecondsSinceEpoch; final fileName = '$prefix$timestamp${suffix ?? ''}'; return File(path.join(tempDir.path, fileName)); } /// 清理临时文件 static Future cleanupTempFiles(String pattern) async { try { final tempDir = Directory.systemTemp; final regex = RegExp(pattern); await for (final entity in tempDir.list()) { if (entity is File) { final fileName = getFileName(entity.path); if (regex.hasMatch(fileName)) { await entity.delete(); } } } } on Object catch (e) { _logger.warning('清理临时文件失败: $e'); } } /// 获取项目根目录下的generator目录路径(兼容旧版本) static String getProjectRootGeneratorDir() { final currentDir = Directory.current.path; return joinPath([currentDir, 'generator']); } /// 安全地写入文件(兼容旧版本) static Future writeFile(String path, String content) async { await safeWriteFile(path, content); } /// 获取目录中的文件列表(兼容旧版本) static Future> listDirectory(String path) async { try { final directory = Directory(path); if (!directory.existsSync()) { return []; } return await directory.list().toList(); } on Object { return []; } } }