495 lines
13 KiB
Dart
495 lines
13 KiB
Dart
import 'dart:io';
|
||
|
||
import 'package:logging/logging.dart';
|
||
import 'package:path/path.dart' as path;
|
||
import 'package:swagger_generator_flutter/utils/path_resolver.dart';
|
||
|
||
/// 文件工具类
|
||
/// 提供文件操作、目录管理和代码格式化功能
|
||
class FileUtils {
|
||
static final Logger _logger = Logger('FileUtils');
|
||
|
||
/// 解析路径(支持相对路径和绝对路径)
|
||
/// 如果是相对路径,相对于项目根目录(配置文件所在目录)
|
||
static String resolvePath(String filePath) {
|
||
return PathResolver.resolvePath(filePath);
|
||
}
|
||
|
||
/// 确保目录存在
|
||
static Future<Directory> ensureDirectoryExists(String dirPath) async {
|
||
final resolvedPath = resolvePath(dirPath);
|
||
final directory = Directory(resolvedPath);
|
||
if (!directory.existsSync()) {
|
||
await directory.create(recursive: true);
|
||
}
|
||
return directory;
|
||
}
|
||
|
||
/// 安全写入文件
|
||
static Future<void> 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<String> 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 bool fileExists(String filePath) {
|
||
return File(filePath).existsSync();
|
||
}
|
||
|
||
/// 检查目录是否存在
|
||
static bool directoryExists(String dirPath) {
|
||
return Directory(dirPath).existsSync();
|
||
}
|
||
|
||
/// 删除文件(如果存在)
|
||
static Future<void> deleteFileIfExists(String filePath) async {
|
||
final file = File(filePath);
|
||
if (file.existsSync()) {
|
||
await file.delete();
|
||
}
|
||
}
|
||
|
||
/// 删除目录(如果存在)
|
||
static Future<void> deleteDirectoryIfExists(String dirPath) async {
|
||
final directory = Directory(dirPath);
|
||
if (directory.existsSync()) {
|
||
await directory.delete(recursive: true);
|
||
}
|
||
}
|
||
|
||
/// 复制文件
|
||
static Future<void> 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<void> 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<int> getFileSize(String filePath) async {
|
||
try {
|
||
final file = File(filePath);
|
||
if (!file.existsSync()) {
|
||
return 0;
|
||
}
|
||
return await file.length();
|
||
} on Object {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/// 获取目录大小
|
||
static Future<int> getDirectorySize(String dirPath) async {
|
||
try {
|
||
final directory = Directory(dirPath);
|
||
if (!directory.existsSync()) {
|
||
return 0;
|
||
}
|
||
|
||
var totalSize = 0;
|
||
await for (final entity in directory.list(recursive: true)) {
|
||
if (entity is File) {
|
||
totalSize += await entity.length();
|
||
}
|
||
}
|
||
return totalSize;
|
||
} on Object {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/// 列出目录中的文件
|
||
static Future<List<String>> listFiles(
|
||
String dirPath, {
|
||
String? extension,
|
||
}) async {
|
||
try {
|
||
final directory = Directory(dirPath);
|
||
if (!directory.existsSync()) {
|
||
return [];
|
||
}
|
||
|
||
final files = <String>[];
|
||
await for (final entity in directory.list()) {
|
||
if (entity is File) {
|
||
if (extension == null || entity.path.endsWith(extension)) {
|
||
files.add(entity.path);
|
||
}
|
||
}
|
||
}
|
||
return files;
|
||
} on Object {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/// 列出目录中的子目录
|
||
static Future<List<String>> listDirectories(String dirPath) async {
|
||
try {
|
||
final directory = Directory(dirPath);
|
||
if (!directory.existsSync()) {
|
||
return [];
|
||
}
|
||
|
||
final directories = <String>[];
|
||
await for (final entity in directory.list()) {
|
||
if (entity is Directory) {
|
||
directories.add(entity.path);
|
||
}
|
||
}
|
||
return directories;
|
||
} on Object {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/// 创建备份文件
|
||
static Future<String> 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<void> 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<String> 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<String> 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<void> batchOperation(
|
||
List<String> filePaths,
|
||
Future<void> Function(String filePath) operation,
|
||
) async {
|
||
for (final filePath in filePaths) {
|
||
try {
|
||
await operation(filePath);
|
||
} on Object catch (e) {
|
||
_logger.warning('批量操作失败: $filePath - $e');
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 查找文件
|
||
static Future<List<String>> findFiles(
|
||
String searchPath,
|
||
String pattern, {
|
||
bool recursive = false,
|
||
}) async {
|
||
try {
|
||
final directory = Directory(searchPath);
|
||
if (!directory.existsSync()) {
|
||
return [];
|
||
}
|
||
|
||
final regex = RegExp(pattern);
|
||
final foundFiles = <String>[];
|
||
|
||
await for (final entity in directory.list(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<DateTime?> 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<bool> 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<String?> 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<File> 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<void> 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<void> writeFile(String path, String content) async {
|
||
await safeWriteFile(path, content);
|
||
}
|
||
|
||
/// 获取目录中的文件列表(兼容旧版本)
|
||
static Future<List<FileSystemEntity>> listDirectory(String path) async {
|
||
try {
|
||
final directory = Directory(path);
|
||
if (!directory.existsSync()) {
|
||
return [];
|
||
}
|
||
return await directory.list().toList();
|
||
} on Object {
|
||
return [];
|
||
}
|
||
}
|
||
}
|