feat(packages): 支持AAB/APKS/APKM/XAPK格式安装包解析

新增AndroidPackageTypeDetector用于识别安装包类型,扩展ApkMetaParser以支持AAB、APKS、APKM、XAPK等容器格式的元数据解析,并在上传服务中集成解析逻辑,完善应用包信息提取能力。
This commit is contained in:
tongtong committed 2026-08-07 19:54:16 +08:00
1 parent 8ee05af6c7
commit 3422d546c5
6 files changed
+624 -37

No files matched your search

@@ -0,0 +1,116 @@
package com.youlai.boot.app.packages.component;
import com.youlai.boot.common.util.FileUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* APK 元数据解析器测试(批量)。
* <p>
* 从 {@link #APK_FILE_LIST} 中读取本地 apk 文件路径,逐个解析并打印元数据。
* 也可通过系统属性 / 环境变量批量覆盖:
* <pre>
* - 系统属性(分号分隔): -Dapk.test.files=D:/a.apk;D:/b.apk
* - 环境变量(分号分隔): APK_TEST_FILES=D:/a.apk;D:/b.apk
* </pre>
* 若没有可解析的文件,测试会打印提示并跳过,不会失败。
*/
@DisplayName("APK 元数据解析器批量测试")
class ApkMetaParserTest {
/**
* 待测试的本地 apk 文件列表(在此处填入本地路径即可批量测试)。
*/
private static final List<String> APK_FILE_LIST = Arrays.asList(
"D:\\apkTest\\com.google.android.youtube.apk",
"D:\\apkTest\\XwadOSHonor_V1.4.7.aab",
"D:\\apkTest\\XwadOSHonor_V1.4.7.apks",
"D:\\apkTest\\Twitter_v8.95.0-release.00_apkpure.com.xapk",
"D:\\apkTest\\com.instagram.android_440.1.0.46.86-384611414_1dpi_c0d8039a2b3d4f69b31e6cbb1c611c5a_apkmirror.com.apkm",
"D:\\apkTest\\抖音V39.1.2谷歌版.apks"
);
private static final String PROP_KEY = "apk.test.files";
private static final String ENV_KEY = "APK_TEST_FILES";
private List<String> resolveApkPaths() {
String raw = System.getProperty(PROP_KEY);
if (raw == null || raw.isBlank()) {
raw = System.getenv(ENV_KEY);
}
if (raw != null && !raw.isBlank()) {
return Arrays.stream(raw.split(";"))
.map(String::trim)
.filter(s -> !s.isBlank())
.toList();
}
return APK_FILE_LIST;
}
@Test
@DisplayName("批量解析本地 apk 文件并打印元数据")
void parseLocalApks() throws IOException {
List<String> paths = resolveApkPaths();
if (paths.isEmpty()) {
System.out.println("===== 跳过测试 =====");
System.out.println("未配置任何待测试的 apk 文件,请通过以下方式之一提供:");
System.out.println(" 1. 在 ApkMetaParserTest.APK_FILE_LIST 中填入本地路径列表");
System.out.println(" 2. 系统属性 -Dapk.test.files=D:/a.apk;D:/b.apk");
System.out.println(" 3. 环境变量 APK_TEST_FILES=D:/a.apk;D:/b.apk");
return;
}
ApkMetaParser parser = new ApkMetaParser();
int total = paths.size();
int success = 0;
for (String path : paths) {
File apkFile = new File(path);
System.out.println();
System.out.println(">>>>>> [" + (success + 1) + "/" + total + "] " + apkFile.getAbsolutePath());
if (!apkFile.exists() || !apkFile.isFile()) {
System.out.println("!! 跳过:文件不存在或不是普通文件");
continue;
}
try {
System.out.println("文件魔数为: " + FileUtils.getMagicBytes(apkFile.getAbsolutePath(), 0, 4));
System.out.println("文件类型为: " + new AndroidPackageTypeDetector().detect(apkFile.toPath()));
ApkMetaParser.ApkMetaResult result = parser.parse(apkFile);
assertNotNull(result, "解析结果不应为空");
assertNotNull(result.getPackageName(), "包名不应为空");
System.out.println("===== APK 元数据 =====");
System.out.println("packageName : " + result.getPackageName());
System.out.println("appLabel : " + result.getAppLabel());
System.out.println("versionName : " + result.getVersionName());
System.out.println("versionCode : " + result.getVersionCode());
System.out.println("minSdk : " + result.getMinSdk());
System.out.println("targetSdk : " + result.getTargetSdk());
System.out.println("compileSdk : " + result.getCompileSdk());
System.out.println("maxSdk : " + result.getMaxSdk());
System.out.println("abiList : " + result.getAbiList());
System.out.println("permissions : " + result.getPermissions());
System.out.println("iconPath : " + result.getIconPath());
System.out.println("iconBytes : " + (result.getIconBytes() == null
? "null" : result.getIconBytes().length + " bytes"));
System.out.println("=====================");
success++;
} catch (Exception e) {
System.out.println("!! 解析失败:" + e.getMessage());
}
}
System.out.println();
System.out.println("===== 批量测试完成:成功 " + success + " / 共 " + total + " 个 =====");
}
}