Java生成ZIP包在Windows10下非ASCII字符被替换为问号问题咨询
问题原因分析
- Windows原生ZIP解压模块的编码兼容缺陷
你已在初始化ZipOutputStream时指定了UTF-8编码,但Windows 10自带的资源管理器ZIP解压逻辑默认会使用系统区域对应的ANSI编码(简体中文系统为GBK)解析压缩包内容,只有检测到ZIP通用标识位的第11位(Unicode编码标记)被开启时,才会使用UTF-8解析。Java的ZipOutputStream默认不会主动开启这个标记,就会导致原生解压工具误解析编码,非ASCII字符被替换为?。 - 代码存在损坏ZIP结构的错误逻辑
你在Settings.PROTECTION判断分支下设置CRC和文件大小的逻辑完全不符合规范:- 用于赋值的
bytes是固定长度为1024的读缓冲区,bytes.length永远等于1024,和实际写入的文件大小完全不匹配 new BigInteger(bytes)取的是最后一次读操作的缓冲区内容转成的大整数,也不是文件的实际大小- 错误的CRC和大小属性会导致整个ZIP结构损坏,Windows原生解压工具解析损坏的压缩包时会进一步触发字符解析异常。
- 用于赋值的
修复方案
- 开启UTF-8编码标识
初始化ZipOutputStream后新增如下代码,开启Unicode编码标记,告知解压工具当前压缩包使用UTF-8编码:
zos.setUseLanguageEncodingFlag(true);
Windows 10 1809及以上版本的原生解压工具识别到该标记后,就会正常使用UTF-8解析内容,不会出现字符替换问题。
- 修复元数据设置逻辑
直接删除if (!(Settings.PROTECTION.toBool()))分支下的两行属性设置代码即可,ZipOutputStream会自动计算并写入正确的CRC和文件大小属性。如果确实需要自定义校验逻辑,需要在写入流的过程中累计总字节数,同时用CRC32类计算实际内容的CRC值后再进行设置。 - 低版本系统兼容
如果需要兼容1809以下版本的Windows 10,可提示用户使用7-Zip、WinRAR等第三方解压工具,这类工具会自动检测ZIP的编码格式,不存在系统原生工具的兼容问题。
内容的提问来源于stack exchange,提问作者Th0rgal
相关产品推荐
相关产品推荐

