feat(packages): 支持AAB/APKS/APKM/XAPK格式安装包解析
新增AndroidPackageTypeDetector用于识别安装包类型,扩展ApkMetaParser以支持AAB、APKS、APKM、XAPK等容器格式的元数据解析,并在上传服务中集成解析逻辑,完善应用包信息提取能力。
This commit is contained in:
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 + " 个 =====");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user