spring boot jar 启动报错 Zip64 archives are not supported

news2024/11/25 1:02:00

spring boot jar 启动报错 Zip64 archives are not supported

  • 原因、解决方案
  • 问题
    • 为什么 spring boot 不支持 zip64
    • zip、zip64 功能上的区别
    • zip 的文件格式
    • spring-boot-loader 是如何判断是否是 zip64 的?
  • 参考

spring boot 版本是 2.1.8.RELEASE,引入以下 phoenix 依赖之后启动报错。

<dependency>
    <groupId>org.apache.phoenix</groupId>
    <artifactId>phoenix-client-hbase-2.4</artifactId>
    <version>5.1.3</version>
</dependency>

错误日志:

PS D:\project\java\zip64\target> java -jar .\zip64-0.0.1-SNAPSHOT.jar
Exception in thread "main" java.lang.IllegalStateException: Failed to get nested archive for entry BOOT-INF/lib/phoenix-client-hbase-2.4-5.1.3.jar
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:108)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:87)
        at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:69)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)
Caused by: java.io.IOException: Unable to open nested jar file 'BOOT-INF/lib/phoenix-client-hbase-2.4-5.1.3.jar'
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:258)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:244)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:104)
        ... 4 more
Caused by: java.lang.IllegalStateException: Zip64 archives are not supported
        at org.springframework.boot.loader.jar.CentralDirectoryEndRecord.getNumberOfRecords(CentralDirectoryEndRecord.java:121)
        at org.springframework.boot.loader.jar.JarFileEntries.visitStart(JarFileEntries.java:117)
        at org.springframework.boot.loader.jar.CentralDirectoryParser.visitStart(CentralDirectoryParser.java:85)
        at org.springframework.boot.loader.jar.CentralDirectoryParser.parse(CentralDirectoryParser.java:56)
        at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:125)
        at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:112)
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:289)
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:266)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:255)

原因、解决方案

Google 很快就找到了原因,stackoverflow 上有类似的问题 java - Add more than 65535 entries jar in Spring boot - Stack Overflow。

第一个回答给出了原因:spring boot 不支持一个 jar 文件中多于 65534(这里应该写错了,应该是 65535) 个文件,并附上了抛异常的代码。

第二个回答是 spring boot 的 issues,有兴趣的可以自己看一下 Support zip64 format executable archives · Issue #2895 · spring-projects/spring-boot (github.com)

第三个回答给出了解决办法:升级到 2.2.x,也给出了支持 zip64 的提交记录 Support zip64 jars by cvienot · Pull Request #16091 · spring-projects/spring-boot (github.com)。我升级成 2.2.0.RELEASE 确实解决了问题。

image-20240625151243205

问题

回答一中的代码来自 spring-boot-loader 子项目中的 org.springframework.boot.loader.jar.CentralDirectoryEndRecord#getNumberOfRecords 方法,依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
    <version>2.1.8.RELEASE</version>
</dependency>

为什么 spring boot 不支持 zip64

从 ZIP (file format) - Wikipedia 中可以看出 zip、zip64 在文件的格式上是不同的。猜测应该是开发者没想到 jar 包里的文件个数或 jar 包的大小会超过 65535,所以没有实现 zip64 相关的。从以下提交中能看出一二。直到最后的两个提交才有人实现了 zip64 的相关代码。

image-20240625173000853

zip、zip64 功能上的区别

zip64 格式是标准 zip 格式的扩展,实际上消除了 zip 存档中文件大小和数量的限制。

每种格式允许的最大值总结如下:

Standard FormatZip64 Format
Number of Files Inside an Archive65,5352^64 - 1
Size of a File Inside an Archive [bytes]4,294,967,2952^64 - 1
Size of an Archive [bytes]4,294,967,2952^64 - 1
Number of Segments in a Segmented Archive999 (spanning) 65,535 (splitting)4,294,967,295 - 1
Central Directory Size [bytes]4,294,967,2952^64 - 1

zip 的文件格式

zip格式压缩包主要由三大部分组成:数据区中央目录记录区(也有叫核心目录记录)中央目录记录尾部区

数据区是由一系列本地文件记录组成,本地文件记录主要是记录了压缩前后文件的元数据以及存放压缩后的文件

中央目录记录区是有一系列中央目录记录所组成,一条中央目录记录对应数据区中的一个压缩文件记录

中央目录记录尾部(End of central directory record)主要作用是用来定位中央目录记录区的开始位置,同时记录压缩包的注释内容

End of central directory record (EOCD)

OffsetBytesDescription[33]中文
04End of central directory signature = 0x06054b50签名
42Number of this disk (or 0xffff for ZIP64)
62Disk where central directory starts (or 0xffff for ZIP64)
82Number of central directory records on this disk (or 0xffff for ZIP64)
102Total number of central directory records (or 0xffff for ZIP64)文件数量(ZIP64 为 0xffff )
124Size of central directory (bytes) (or 0xffffffff for ZIP64)
164Offset of start of central directory, relative to start of archive (or 0xffffffff for ZIP64)
202Comment length (n)注释长度
22nComment

spring-boot-loader 是如何判断是否是 zip64 的?

// 从 bytes 的 offset 偏移量开始,以小端模式读取 length 个字节
public static long littleEndianValue(byte[] bytes, int offset, int length) {
    long value = 0;
    for (int i = length - 1; i >= 0; i--) {
       value = ((value << 8) | (bytes[offset + i] & 0xFF));
    }
    return value;
}
/**
 * A ZIP File "End of central directory record" (EOCD).
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @see <a href="https://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a>
 */
class CentralDirectoryEndRecord {

	// EOCD 最小长度,从表中可以看出在没有注释的情况下是 22
	private static final int MINIMUM_SIZE = 22;

	// 从表中可以看出注释长度为 2 字节,所有最大值是 65535
	private static final int MAXIMUM_COMMENT_LENGTH = 0xFFFF;

	private static final int MAXIMUM_SIZE = MINIMUM_SIZE + MAXIMUM_COMMENT_LENGTH;

	// EOCD 开始的标记
	private static final int SIGNATURE = 0x06054b50;

	// EOCD 中“注释长度”字段的偏移量,从表中可以看出是 20
	private static final int COMMENT_LENGTH_OFFSET = 20;

	// 每次从文件尾部读取 256 字节
	private static final int READ_BLOCK_SIZE = 256;

	// 最终是 EOCD 的字节数组
	private byte[] block;

	// EOCD 在 block 中的偏移量
	private int offset;

	// EOCD 的字节数
	private int size;

	/**
	 * Create a new {@link CentralDirectoryEndRecord} instance from the specified
	 * {@link RandomAccessData}, searching backwards from the end until a valid block is
	 * located.
	 * @param data the source data
	 * @throws IOException in case of I/O errors
	 */
	CentralDirectoryEndRecord(RandomAccessData data) throws IOException {
		// 从文件尾部读取 256 字节
		this.block = createBlockFromEndOfData(data, READ_BLOCK_SIZE);
		this.size = MINIMUM_SIZE;
		this.offset = this.block.length - this.size;
		// 尝试找到 EOCD 的开头
		while (!isValid()) {
			this.size++;
			if (this.size > this.block.length) {
				if (this.size >= MAXIMUM_SIZE || this.size > data.getSize()) {
					throw new IOException(
							"Unable to find ZIP central directory " + "records after reading " + this.size + " bytes");
				}
				// 每次多读 1 字节
				this.block = createBlockFromEndOfData(data, this.size + READ_BLOCK_SIZE);
			}
			// offset 每次向前移动 1 字节
			this.offset = this.block.length - this.size;
		}
	}

	private byte[] createBlockFromEndOfData(RandomAccessData data, int size) throws IOException {
		int length = (int) Math.min(data.getSize(), size);
		return data.read(data.getSize() - length, length);
	}

	// 尝试找到 EOCD 的开头
	private boolean isValid() {
		// 长度小于 EOCD 的最小长度,肯定不符合
		if (this.block.length < MINIMUM_SIZE
				// 读取 block 最开始的 4 个字节,与 EOCD 的标记进行比较,不符合则返回 false
				// 如果相等则找到了 EOCD 的开头
				|| Bytes.littleEndianValue(this.block, this.offset + 0, 4) != SIGNATURE) {
			return false;
		}
		// 读取注释长度 2 字节
		// Total size must be the structure size + comment
		long commentLength = Bytes.littleEndianValue(this.block, this.offset + COMMENT_LENGTH_OFFSET, 2);
		// EOCD 的字节数肯定等于 EOCD 的最小长度 + 注释内容的长度
		return this.size == MINIMUM_SIZE + commentLength;
	}

	/**
	 * Return the number of ZIP entries in the file.
	 * @return the number of records in the zip
	 */
	public int getNumberOfRecords() {
		// 读取 block 偏移量文 10 的 2 个字节,即文件数量
		long numberOfRecords = Bytes.littleEndianValue(this.block, this.offset + 10, 2);
		// 如果文件数量为 65535 则为 Zip64
		if (numberOfRecords == 0xFFFF) {
			throw new IllegalStateException("Zip64 archives are not supported");
		}
		return (int) numberOfRecords;
	}

}

参考

  • java - Add more than 65535 entries jar in Spring boot - Stack Overflow
  • 压缩包Zip格式详析(全网最详细)_zip格式详解-CSDN博客
  • ZIP文件格式分析 | Sp4n9x’s Blog
  • ZIP (file format) - Wikipedia

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1861363.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Chrome开发者工具学习

打开开发者工具&#xff1a; 可以通过在网页上点击右键并选择“检查”来打开。 或者使用快捷键Ctrl Shift I&#xff08;在Windows/Linux上&#xff09;或Command Option I&#xff08;在Mac上&#xff09;。 界面概览&#xff1a; 熟悉DevTools的基本面板&#xff0c;如“…

Mongo Express 未授权访问漏洞

【产品&&漏洞简述】 Mongo Express 是一个基于 Node.js 和 express 的开源的 MongoDB Web管理界面。Mongo Express存在未授权访问漏洞&#xff0c;攻击者可通过该漏洞获取用户信息或修改系统数据。 【资产测绘Query】 title"Home - Mongo Express" 【产品界…

期货交易中的几种常见心态管理

期货交易通常涉及到风险和收益的权衡&#xff0c;因此参与者的心态可以显著影响他们的决策和最终结果。以下是一些炒期货的常见心态&#xff1a; 1. 利润最大化心态&#xff1a;持有这种心态的投资者不关心风险&#xff0c;只考虑高利润。他们可能会盲目追求高回报&#xff0…

【D3.js in Action 3 精译】1.1.3 D3.js 的工作原理

译者注 上一节我们探讨了 D3.js 的适用场景——需要高度定制化、可以尽情释放想象力的复杂图表。这一节我们再跟随作者的视角&#xff0c;看看 D3.js 的工作原理究竟是怎样的。 1.1.3 D3.js 的工作原理 您可能已经体验过 D3 并且发现它不太容易上手。这也许是因为您把它当成了…

【Linux】使用信号进行进程间通信

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 ​ ​ 实现原理&a…

互联网直播/点播技术与平台创新应用:视频推拉流EasyDSS案例分析

随着互联网技术的快速发展&#xff0c;直播/点播平台已成为信息传播和娱乐的重要载体。特别是在电视购物领域&#xff0c;互联网直播/点播平台与技术的应用&#xff0c;不仅为用户带来了全新的购物体验&#xff0c;也为商家提供了更广阔的营销渠道。传统媒体再一次切实感受到了…

QT学习积累——在C++中,for循环中使用``与不使用``的区别和联系

目录 引出使用&与不使用&除法的一个坑 总结自定义信号和槽1.自定义信号2.自定义槽3.建立连接4.进行触发 自定义信号重载带参数的按钮触发信号触发信号拓展 lambda表达式返回值mutable修饰案例 引出 QT学习积累——在C中&#xff0c;for循环中使用&与不使用&的…

MATLAB算法实战应用案例精讲-【数模应用】线性判别分析(附MATLAB、python和R语言代码实现)

目录 前言 算法原理 什么是判别分析 线性判别分析(LDA) 数学模型 二分类 多分类LDA ​编辑 算法思想: 费歇(FISHER)判别思想 贝叶斯(BAYES)判别思想 LDA算法流程 LDA与PCA对比 SPSSPRO 1、作用 2、输入输出描述 3、案例示例 4、案例数据 5、案例操作 …

新手教学系列——“笑看”单元测试(pytest)

探索单元测试的必要性 你是否曾经思考过,当前的业务场景是否真的需要单元测试?我们每个人负责的模块是否也需要单元测试?什么阻碍了我们进行单元测试呢?时间紧,任务重,还要写测试?这些都是我们在开发过程中常见的问题。假设我们有一个计划开发一周的项目,让我们看看有…

rapidocr-onnxruntime库及在open-webui上传PDF 图像处理 (使用 OCR)应用

背景 rapidocr-onnxruntime是一个跨平台的OCR库&#xff0c;基于ONNXRuntime推理框架。 目前已知运行速度最快、支持最广&#xff0c;完全开源免费并支持离线快速部署的多平台多语言OCR。 缘起&#xff1a;百度paddle工程化不是太好&#xff0c;为了方便大家在各种端上进行oc…

计算机网络课程实训:局域网方案设计与实现(基于ensp)

文章目录 前言基本要求操作分公司1分公司2总部核心交换机配置实现内部服务器的搭建acl_deny部分用户与服务器出口出口防火墙配置 前言 本篇文章是小编实训部分内容&#xff0c;内容可能会有错误&#xff0c;另外ensp对电脑兼容性及其挑剔&#xff0c;在使用之前一定要安装好。…

第100+13步 ChatGPT学习:R实现决策树分类

基于R 4.2.2版本演示 一、写在前面 有不少大佬问做机器学习分类能不能用R语言&#xff0c;不想学Python咯。 答曰&#xff1a;可&#xff01;用GPT或者Kimi转一下就得了呗。 加上最近也没啥内容写了&#xff0c;就帮各位搬运一下吧。 二、R代码实现决策树分类 &#xff08;…

极客之夜 | XCTF国际网络攻防联赛十周年庆典圆满落幕

在数字化浪潮的推动下&#xff0c;网络安全已成为全球关注的焦点。十年磨一剑&#xff0c;XCTF国际网络攻防联赛以其卓越的赛事品质和深远的影响力&#xff0c;成为网络安全领域的一面旗帜。极客之夜&#xff0c;我们齐聚一堂&#xff0c;共同庆祝XCTF的十年辉煌&#xff0c;展…

andon系统及时通知对应处理人员,助力产线快速处理异常

在当今快节奏、高效率的制造业环境中&#xff0c;生产线的稳定运行和快速响应异常情况的能力对于企业的成功至关重要。Andon 系统作为一种先进的生产管理工具&#xff0c;凭借其及时通知对应处理人员的功能&#xff0c;为产线快速处理异常提供了强大的助力。 一、Andon系统组成…

基于springboot+Vue高校宿舍管理系统的设计与实现【附源码】

本科毕业设计&#xff08;论文&#xff09; 基于springbootVue高校宿舍管理系统的设计与实现 目录 摘要 2 第一章 绪论 2 1.1 开发背景 2 1.2 开发意义 2 第二章 系统分析 3 2.1 系统的需求分析 3 2.2 系统开发设计思想 3 2.3系统开发步骤 3 2.4 系统的主要技术 4 2.4.1 B/S系…

分享一个好用的图幅号计算器

如果在你的工作中会分幅处理地图数据&#xff0c;也许这个好用的图幅号计算器能对你有所帮助。 你只需要在该工具中输入经纬度坐标&#xff0c;就可以为你计算出各个比例尺下的图幅号&#xff0c;你可以在文末查看该工具的领取方法。 一个好用的图幅号计算器 该图幅计算器工…

3d渲染软件有哪些(1),渲染100邀请码1a12

3D渲染是把三维模型转成2D图像的过程&#xff0c;领域不同常用的软件也不一样&#xff0c;今天我们就简单介绍几个。 在介绍前我们先推荐一个设计人员常用到的工具&#xff0c;就是网渲平台渲染100&#xff0c;通过它设计师可以把本地渲染放到云端进行&#xff0c;价格也不贵&a…

全景vr交互微课视频开发让学习变得更加有趣、高效

在数字化教育的浪潮中&#xff0c;3D虚拟微课系统操作平台以其独特的魅力和创新的功能&#xff0c;成为吸引学生目光的焦点。这个平台不仅提供了引人入胜的画面和内容丰富的课件&#xff0c;更通过技术革新和制作方式的探索&#xff0c;将课程制作推向了一个全新的高度。 随着技…

基于流量特征DNS隐蔽信道分析方法总结

一、 概述 隐蔽信道是指允许进程以危害系统安全策略的方式传输倍息的通信通道。隐蔽信道在公开的信道掩盖下&#xff0c;采用特殊的编码方式&#xff0c;传输非法或私密的信息而不被人发现。其广泛存在于操作系统、网络系统和应用系统中。对网络信息系统的安全构成了严重威…

U盘数据恢复宝典:从原因到解决方案的全面指南

一、U盘数据恢复概述 在日常生活和工作中&#xff0c;U盘已成为我们不可或缺的数据存储工具。然而&#xff0c;随着数据量的不断增长和使用的频繁&#xff0c;U盘数据丢失的问题也日益突出。U盘数据恢复技术正是在这种背景下应运而生&#xff0c;它通过各种技术手段找回因误删…