@@ -2,117 +2,106 @@ 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 ;
/// 获取应用根目录
/// 在发布模式下,返回 .exe 文件所在目录
/// 在开发模式下,返回 windows 目录(因为 bin 文件夹在 windows/bin 下)
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 目录
// 检查是否是开发模式(可执行文件在 build 目录下)
if ( exeDir . contains ( ' build ' ) | | exeDir . contains ( ' windows \\ build ' ) | | exeDir . contains ( ' windows/build ' ) ) {
// 开发模式: bin 文件夹在 windows/bin 下
// 从可执行文件路径向上查找 windows 目录
var currentPath = exeDir ;
var foundWindows = false ;
// 向上查找,直到找到包含 'windows' 且不是 'build\windows' 的目录
while ( currentPath . isNotEmpty ) {
final dirName = p . basename ( currentPath ) ;
final parent = p . dirname ( currentPath ) ;
if ( dirName = = ' windows ' & &
! currentPath . contains ( ' build \\ windows ' ) & &
! currentPath . contains ( ' build/windows ' ) ) {
// 如果当前目录名是 'windows',且不在 build 路径中,说明找到了项目根目录下的 windows 目录
if ( dirName = = ' windows ' & & ! currentPath . contains ( ' build \\ windows ' ) & & ! currentPath . contains ( ' build/windows ' ) ) {
// 检查这个 windows 目录下是否有 bin 文件夹
final binPath = p . join ( currentPath , ' bin ' ) ;
if ( Directory ( binPath ) . existsSync ( ) ) {
return currentPath ;
_cachedAppRoot = currentPath ;
foundWindows = true ;
break ;
}
}
if ( parent = = currentPath ) break ;
if ( parent = = currentPath ) break ; // 到达根目录
currentPath = parent ;
}
// 回退到通过当前工作目录查找
final fromCwd = _findDirWithBinFromCwd ( ' windows ' ) ;
if ( fromCwd ! = null ) return fromCwd ;
if ( foundWindows ) {
return _cachedAppRoot ! ;
}
// 如果向上查找失败,尝试从当前工作目录查找
final currentDir = Directory . current . path ;
if ( currentDir . contains ( ' windows ' ) ) {
// 找到 windows 目录
final parts = currentDir . split ( RegExp ( r'[\\/]' ) ) ;
var windowsIndex = - 1 ;
for ( int i = 0 ; i < parts . length ; i + + ) {
if ( parts [ i ] = = ' windows ' ) {
windowsIndex = i ;
break ;
}
}
if ( windowsIndex > = 0 ) {
final windowsDir = parts . sublist ( 0 , windowsIndex + 1 ) . join ( Platform . pathSeparator ) ;
final binPath = p . join ( windowsDir , ' bin ' ) ;
if ( Directory ( binPath ) . existsSync ( ) ) {
_cachedAppRoot = windowsDir ;
return _cachedAppRoot ! ;
}
}
}
}
// 发布模式:可执行文件和 bin 文件夹在同一目录
return exeDir ;
_cachedAppRoot = exeDir ;
return _cachedAppRoot ! ;
} catch ( e ) {
// 如果获取失败,尝试使用当前工作目录
final currentDir = Directory . current . path ;
if ( currentDir . contains ( ' windows ' ) ) {
// 找到 windows 目录
final parts = currentDir . split ( RegExp ( r'[\\/]' ) ) ;
var windowsIndex = - 1 ;
for ( int i = 0 ; i < parts . length ; i + + ) {
if ( parts [ i ] = = ' windows ' ) {
windowsIndex = i ;
break ;
}
}
/// Linux 平台下解析根目录
static String _resolveLinuxAppRoot ( String exeDi r) {
// 如果可执行文件目录下有 bin, 优先使用( 发布模式)
final binInExeDir = p . join ( exeDir , ' bin ' ) ;
if ( Directory ( binInExeDir ) . existsSync ( ) ) {
return exeDir ;
if ( windowsIndex > = 0 ) {
final windowsDir = parts . sublist ( 0 , windowsIndex + 1 ) . join ( Platform . pathSeparato r ) ;
final binPath = p . join ( windowsDir , ' bin ' ) ;
if ( Directory ( binPath ) . existsSync ( ) ) {
_cachedAppRoot = windowsDir ;
return _cachedAppRoot ! ;
}
// 开发模式:从当前工作目录向上查找包含 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 ;
_cachedAppRoot = currentDir ;
return _cachedAppRoot ! ;
}
final parent = p . dirname ( currentPath ) ;
if ( parent = = currentPath ) break ;
currentPath = parent ;
}
return null ;
}
/// 获取 bin 目录路径