extractAsset function
从 assets 解压文件 (增强版)
rename 支持两种模式:
- 仅文件名 (e.g. "singbox.exe") -> 自动存入 AppData 根目录
- 绝对路径 (e.g. "C:\Users...\bin\singbox.exe") -> 存入指定位置
Implementation
Future<String> extractAsset(String assetPath, {String? rename, bool update = false}) async {
String targetPath;
// --- 逻辑升级 Start ---
if (rename != null && p.isAbsolute(rename)) {
// 模式 A: 传入的是绝对路径,直接信赖
targetPath = rename;
} else {
// 模式 B: 传入的是文件名或相对路径,拼接到 AppData
Directory appDir = await getApplicationSupportDirectory();
String fileName = rename ?? p.basename(assetPath);
targetPath = p.join(appDir.path, fileName);
}
File file = File(targetPath);
// 关键修复:确保父目录存在!(否则 bin/singbox.exe 会报错)
if (!file.parent.existsSync()) {
file.parent.createSync(recursive: true);
}
// --- 逻辑升级 End ---
// 检查文件是否存在
if (!update && await file.exists()) {
devlog('[extractAsset] ⏭️ 文件已存在,跳过: ${p.basename(targetPath)}');
if (!Platform.isWindows) {
await Process.run('chmod', ['+x', targetPath]);
}
return targetPath;
}
// 读取并写入
try {
ByteData data = await rootBundle.load(assetPath);
await file.writeAsBytes(data.buffer.asUint8List());
// Linux/Mac 赋予执行权限
if (!Platform.isWindows) {
await Process.run('chmod', ['+x', targetPath]);
}
devlog('[extractAsset] ✅ 已解压: $assetPath -> $targetPath');
} catch (e) {
devlog('[extractAsset] ❌ 解压失败: $e');
rethrow;
}
return targetPath;
}