Skip to content

fix: 修复 Linux arm64 上压缩包无法预览 - #804

Open
yuhaibohotmail wants to merge 1 commit into
kekingcn:masterfrom
yuhaibohotmail:fix/sevenzip-linux-arm64
Open

yuhaibohotmail wants to merge 1 commit into
kekingcn:masterfrom
yuhaibohotmail:fix/sevenzip-linux-arm64

Conversation

@yuhaibohotmail

Copy link
Copy Markdown
Contributor

问题

在 aarch64 的 Linux 上预览任何压缩包(zip、rar、7z 等),页面提示:

该压缩包文件无法处理!

日志:

SevenZipNativeInitializationException: Error loading SevenZipJBinding native library into JVM:
Can't find suited platform for os.arch=aarch64, os.name=Linux... Available list of platforms:
Linux-amd64, Linux-i386, Mac-x86_64, Windows-amd64, Windows-x86

调用栈在 CompressFileReader.unRar → SevenZip.openInArchive。x86_64 的 Linux 与 Windows 上正常。5.0.3(即当前 master)可以复现。

原因

有两处,只解决其中一处都不行:

1. 原生库不在。 server/pom.xml 依赖的 sevenzipjbinding-all-platforms(16.02-2.01)里只有 Linux-amd64、Linux-i386、Mac-x86_64、Windows-amd64、Windows-x86 五个平台的原生库,没有 Linux arm64。Maven Central 上同版本另有一个单独的构件 net.sf.sevenzipjbinding:sevenzipjbinding-linux-arm64:16.02-2.01,里面是 Linux-arm64/lib7-Zip-JBinding.so。

2. 只加依赖,SevenZipJBinding 仍然选不到它。 SevenZip.getPlatformBestMatch 的选法是:平台列表只有一项就用那一项;否则用 os.name 的第一个单词 + - + os.arch 去列表里找完全相同的名字。JVM 在这种机器上的 os.arch 是 aarch64,拼出来是 Linux-aarch64,而原生库所在的目录、平台列表里登记的名字都是 Linux-arm64,匹配不上。

另外两个 jar 各带一份同名的 /sevenzipjbinding-platforms.properties,SevenZipJBinding 只读取类路径上的第一份:如果 arm64 的 jar 排在前面,平台列表只剩 Linux-arm64 一项,x86_64 与 Windows 上也会去加载 arm64 的原生库。所以也不能靠调整依赖顺序来解决。

修复

  • server/pom.xml:引入 sevenzipjbinding-linux-arm64(沿用 ${sevenzipjbinding.version}),放在 sevenzipjbinding-all-platforms 之后,其它平台读到的平台列表与原来相同;
  • 新增 cn.keking.utils.SevenZipNativePlatform:只在 os.name 第一个单词是 Linux 且 os.arch 是 aarch64 时,调用 SevenZip.initSevenZipFromPlatformJAR("Linux-arm64") 显式指定平台名;其它平台直接返回,仍由 SevenZipJBinding 自动选择。初始化失败只记录日志、不向外抛,不影响应用启动(之后真正解压时 SevenZipJBinding 会按原来的方式报错);
  • CompressFileReader:增加静态初始化块调用 SevenZipNativePlatform.init()。解压逻辑没有改动。

没有直接升级到 sevenzipjbinding 23.01-2.2(它的 all-platforms 自带 Linux-arm64):那样 x86_64 与 Windows 上的解压引擎也会一起从 16.02 换成 23.01,all-platforms 的 jar 也从约 7 MB 变成约 24 MB,影响范围比修这个问题大得多,更适合单独评估。本 PR 只增加一个约 2 MB 的 jar,其它平台使用的原生库一个字节都不变。如果维护者更倾向于直接升级版本,我可以改成那种做法。

验证

新增 SevenZipNativePlatformTests,4 条。运行测试的机器通常不是 arm64,无法真正加载该原生库,所以校验的是加载之前的各个环节:

  • shouldShipLinuxArm64NativeLibrary:Linux-arm64 的原生库在类路径上,并且是 AArch64 的 ELF 文件;
  • shouldSelectLinuxArm64PlatformOnLinuxAarch64:Linux + aarch64 时指定的平台名是 Linux-arm64,按这个名字能取到原生库;
  • shouldLeaveOtherPlatformsToAutomaticSelection:其它平台(Linux amd64 / i386 / arm、Windows、macOS)不指定平台名;
  • shouldKeepExistingPlatformsAvailable:原有平台仍在 SevenZipJBinding 读到的平台列表里,原生库仍能取到。

修改前先跑(只放测试与一个空实现):第 1、2 条失败(/Linux-arm64/sevenzipjbinding-lib.properties is not on the classpath、expected: <Linux-arm64> but was: <null>);修改后 4 条全部通过。把平台名的指定去掉,第 2 条重新失败;把两个依赖的顺序对调,第 4 条失败(listed platforms: [Linux-arm64])。

mvn -B -pl server test -Dtest='!ServerMainTests'(Windows,JDK 25):Tests run: 91, Failures: 0, Errors: 1。唯一的 1 条是 ArchiveDirectorySecurityTests.cachedArchivePreviewRendersItsTreeWithoutAnEnumerationRequest,它要创建文件名带双引号的文件,Windows 不允许,在未修改的 master 上同样失败(master 为 87 条、同样 1 条 Error),与本次修改无关。

实际环境:已在 aarch64 的 Linux(容器部署,JDK 25)上用与本 PR 相同的改动(只有注释与日志文案不同)验证——修改前预览压缩包失败并出现上面的日志;修改后 zip 能列出文件树,包内的文件能打开,日志中出现原生库按 Linux-arm64 平台加载成功的那一行。x86_64 的 Linux 上压缩包预览照常。

适用范围

glibc 的 Linux arm64。musl(如 Alpine)与 32 位 ARM 不在本 PR 范围内,16.02-2.01 没有对应的原生库。

在 aarch64 的 Linux 上预览 zip/rar/7z 等压缩包时提示「该压缩包文件无法处理!」,
日志为 SevenZipNativeInitializationException: Can't find suited platform for
os.arch=aarch64, os.name=Linux。

原因有两处:
1. sevenzipjbinding-all-platforms 16.02-2.01 只包含 Linux-amd64、Linux-i386、
   Mac-x86_64、Windows-amd64、Windows-x86 的原生库,没有 Linux arm64。
2. SevenZipJBinding 自动选择平台时用 os.name 的第一个单词 + "-" + os.arch
   拼出 Linux-aarch64,而 arm64 原生库所在的目录是 Linux-arm64,只引入
   原生库仍然匹配不到。

修改:
- 引入同版本的 sevenzipjbinding-linux-arm64,放在 all-platforms 之后
  (两个 jar 各带一份同名的平台列表文件,SevenZipJBinding 只读取类路径上的
  第一份,顺序反过来其它平台也会去加载 arm64 的原生库)。
- 新增 SevenZipNativePlatform,仅在 Linux + aarch64 时调用
  SevenZip.initSevenZipFromPlatformJAR("Linux-arm64");其它平台不做任何处理。
  初始化失败只记录日志,不影响应用启动。
- CompressFileReader 在静态初始化块中调用,解压逻辑不变。

新增 SevenZipNativePlatformTests 4 条。

Signed-off-by: yuhaibo <yuhaibo@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant