Files
windows-application/lib/utils/path_utils.dart
2026-01-29 10:44:29 +08:00

129 lines
4.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:io';
import 'package:path/path.dart' as p;
/// 路径工具类 - 获取应用根目录和 bin 目录
///
/// 约定:
/// - Windows 开发模式:`windows/bin` 目录下放置配置和数据文件
/// - Windows 发布模式:可执行文件同级目录下放置 `bin` 目录
/// - Linux 开发模式:项目根目录下放置 `bin` 目录
/// - Linux 发布模式:可执行文件同级目录下放置 `bin` 目录
class PathUtils {
static String? _cachedAppRoot;
/// 获取应用根目录
static String get appRoot {
if (_cachedAppRoot != null) {
return _cachedAppRoot!;
}
try {
final exePath = Platform.resolvedExecutable;
final exeDir = p.dirname(exePath);
if (Platform.isWindows) {
_cachedAppRoot = _resolveWindowsAppRoot(exeDir);
} else if (Platform.isLinux) {
_cachedAppRoot = _resolveLinuxAppRoot(exeDir);
} else {
// 其他平台暂时按当前工作目录 + bin 处理
_cachedAppRoot = _resolveGenericAppRoot(exeDir);
}
return _cachedAppRoot!;
} catch (_) {
// 兜底:使用当前工作目录
_cachedAppRoot = Directory.current.path;
return _cachedAppRoot!;
}
}
/// Windows 平台下解析根目录
static String _resolveWindowsAppRoot(String exeDir) {
// 开发模式:可执行文件在 build 目录下,需要回溯到 windows 目录
if (exeDir.contains('build') || exeDir.contains('windows\\build') || exeDir.contains('windows/build')) {
var currentPath = exeDir;
while (currentPath.isNotEmpty) {
final dirName = p.basename(currentPath);
final parent = p.dirname(currentPath);
if (dirName == 'windows' &&
!currentPath.contains('build\\windows') &&
!currentPath.contains('build/windows')) {
final binPath = p.join(currentPath, 'bin');
if (Directory(binPath).existsSync()) {
return currentPath;
}
}
if (parent == currentPath) break;
currentPath = parent;
}
// 回退到通过当前工作目录查找
final fromCwd = _findDirWithBinFromCwd('windows');
if (fromCwd != null) return fromCwd;
}
// 发布模式:可执行文件和 bin 文件夹在同一目录
return exeDir;
}
/// Linux 平台下解析根目录
static String _resolveLinuxAppRoot(String exeDir) {
// 如果可执行文件目录下有 bin优先使用发布模式
final binInExeDir = p.join(exeDir, 'bin');
if (Directory(binInExeDir).existsSync()) {
return exeDir;
}
// 开发模式:从当前工作目录向上查找包含 bin 的目录(项目根目录)
final fromCwd = _findDirWithBinFromCwd(null);
if (fromCwd != null) return fromCwd;
// 再退回到 exeDir
return exeDir;
}
/// 通用平台兜底逻辑:优先找当前工作目录上的 bin
static String _resolveGenericAppRoot(String exeDir) {
final fromCwd = _findDirWithBinFromCwd(null);
if (fromCwd != null) return fromCwd;
return exeDir;
}
/// 从当前工作目录向上查找,直到找到满足条件的目录
/// 如果 [expectDirName] 不为空,则必须匹配目录名,例如 'windows'
static String? _findDirWithBinFromCwd(String? expectDirName) {
var currentPath = Directory.current.path;
while (currentPath.isNotEmpty) {
final dirName = p.basename(currentPath);
final binPath = p.join(currentPath, 'bin');
if ((expectDirName == null || dirName == expectDirName) &&
Directory(binPath).existsSync()) {
return currentPath;
}
final parent = p.dirname(currentPath);
if (parent == currentPath) break;
currentPath = parent;
}
return null;
}
/// 获取 bin 目录路径
static String get binDir {
final root = appRoot;
return p.join(root, 'bin');
}
/// 获取 bin 目录下的文件路径
static String binFile(String filename) {
return p.join(binDir, filename);
}
}