Zip文件是如何做到在文件开始和结束部分添加任意长度字节而仍不影响文件识别的?

操作步骤:

  1. 使用任意压缩工具创建一个测试用Zip压缩文件;
  2. 使用如下Java代码读取文件信息:

    public static void main(String[] args) throws Exception {
     ZipFile zipFile = new ZipFile("demo.zip");
     for (ZipEntry entry : Collections.list(zipFile.entries())) {
         System.out.println(entry.getName());
     }
     zipFile.close();
    }
  3. 使用Notepad++打开上面的zip文件,在文件头和尾分别添加任意长度的文字,保存。
  4. 再次执行上面代码,仍然能正确识别压缩文件内容。

但是换用ZipInputStream,可以读取原始Zip文件,后面修改后的文件则读不出来(不报错,读取空白)

public static void main(String[] args) throws Exception {
    ZipInputStream zipin = new ZipInputStream(new FileInputStream("demo.zip"));
    for (ZipEntry entry = zipin.getNextEntry(); entry != null; entry = zipin.getNextEntry()) {
        System.out.println(entry.getName());
    }
    zipin.close();
}

ZipInputStream无法读取修改后的Zip文件,这点查看ZipInputStream.readLOC()代码可以看出原因。因为ZipInputStream是从流的当前字节去匹配Zip文件的LOC结构(即:Reads local file (LOC) header for next entry. 匹配LOC标识字节:50 4B 03 04,如果不匹配则认为找不到Entry)
ZipInputStream 这样处理是可以理解的,因为底层输入流只能读取一次,它无法随机访问底层输入流的任意字节,也就是说无法读取Zip文件的 中心目录区、中心目录区结束标识。只能通过读取 文件数据存储区结构来解析Zip文件。

可自解压的压缩文件,以BandiZip为例,创建的xx.exe文件,实测结果:
如果创建的exe文件指定压缩算法为 仅存储,则用Java的ZipFile是可以识别的,在文件头尾添加任意文字不影响读取。
但是创建exe文件时如果指定了压缩算法,那么用Java的ZipFile会读取报错:
invalid CEN header (bad compression method)

所以,这里的疑问是:ZipFile是怎样识别哪儿是正确的 中心目录区位置 呢?

附:Zip文件的结构参考下面文章:
https://goodapple.top/archives/700

阅读 717
avatarAI BotBETA

Zip文件结构及其识别机制

ZIP文件是一个包含压缩和非压缩文件的归档文件,它有一个特定的文件格式和结构,使得即使添加了额外的字节,只要中心目录区(End of Central Directory, EOCD)没有被破坏,ZIP文件仍然可以被识别和解压。

ZIP文件结构大致如下:

  • 本地文件头(Local File Header, LFH)
  • 文件数据(File Data)
  • (可选的)数据描述符(Data Descriptor)
  • ...(多个文件重复以上结构)
  • 中心目录结构(Central Directory Structure, CDS)
  • 中心目录区结束标识(End of Central Directory Record, EOCD)

ZIP文件识别的关键在于EOCD。EOCD包含指向中心目录结构开始的指针,以及ZIP文件的注释长度等信息。这个结构是ZIP文件的最后一个部分,且有一个固定的签名50 4B 05 06(16进制表示)。

ZipFile是如何识别正确的中心目录区位置的?

ZipFile类在Java中通常是通过查找ZIP文件的EOCD来识别中心目录区的位置。它会在文件的末尾向前搜索,直到找到EOCD的签名。一旦找到EOCD,ZipFile就可以根据EOCD中的信息定位到中心目录结构的开始,并从此处读取文件的条目信息。

为什么添加了额外字节后ZipFile仍能识别?

只要EOCD没有被覆盖或损坏,ZipFile就能通过搜索EOCD找到中心目录结构。即使文件开始或结束添加了额外的字节,也不会影响ZIP文件的识别,因为ZipFile不是从文件开始读取的,而是从文件末尾向前搜索EOCD。

为什么ZipInputStream无法读取修改后的ZIP文件?

ZipInputStream则是从文件的开始处读取,它依赖于在文件中按照预定顺序出现的本地文件头(LFH)来逐个解析ZIP条目。如果在ZIP文件的开始处添加了额外的字节,这些字节可能会破坏LFH的结构,导致ZipInputStream无法正确识别ZIP条目。

关于自解压的exe文件

自解压的exe文件(如BandiZip创建的)实际上是一个包含ZIP文件的可执行文件。当这些文件被创建为“仅存储”模式时,ZIP文件部分的结构保持不变,因此可以被ZipFile识别。但是,如果使用了压缩算法,那么ZIP文件部分的结构可能会发生变化,导致ZipFile无法正确解析。这可能是因为自解压程序对ZIP文件进行了额外的处理或封装,使得它不再符合标准的ZIP文件结构。

总结来说,ZIP文件的识别关键在于EOCD,而ZipFileZipInputStream在处理ZIP文件时的方式不同,导致它们对额外字节的容忍度也不同。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏