当前位置: 首页 > 知识库问答 >
问题:

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

全心思
2024-07-19

操作步骤:

  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

共有1个答案

施越彬
2024-07-19

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文件时的方式不同,导致它们对额外字节的容忍度也不同。

 类似资料:
  • 问题内容: 我正在尝试将一些文件添加到ZIP文件中,它会创建文件,但不会在其中添加任何内容。代码1: 我的功能: 编辑: 我发现了问题,只是在将文件从C:\驱动器写入F:\驱动器的ZIP时遇到麻烦 问题答案: 您不能压缩文件夹,只能压缩文件。要压缩文件夹,必须手动添加所有子文件。我写了本课来完成这项工作。您可以免费获得它:) 用法是这样的: 这是课程: 请享用! 编辑 :要检查程序是否仍在忙,可以

  • 问题内容: 我试图通过在numpy中进行解析/数据累积来加快我去年编写的二进制文件解析器的速度。numpy定义自定义数据结构并将数据从二进制文件导入其中的能力看起来像我所需要的,除了这些文件中的某些字段是“非标准”长度(例如6字节)的无符号整数。由于我使用的是Python 2.7,所以我制作了自己的int.from_bytes仿真版本来处理这些字段,但是如果有任何方法可以将这些字段读取为numpy

  • 我知道,如果创建并写入zip文件,我会将ZipOutputStream环绕在OutputStream(或子类)周围,如下所示: fos=new FileOutputStream(fileName) zOut=new ZipOutputStream(fos); 我还知道如何创建zip文件,以及需要指定何时添加新条目和何时关闭条目。我想做的是加密存档中的文件,但保持存档未加密,这样条目仍然很容易找到。

  • 我试图在ZIP文件内创建一个ZIP文件,以重新构建以前在内存中的zip结构,我在Java。 我失败了,因为我得到了一个错误的内部ZIP内创建的初始ZIP文件。文件已损坏。当试图打开它时,我得到一个“文件的意外结局”。 我得到了这个结构: -input.zip--InnerInput.zip 代码使用java Stack和Map在内存中解压。然后它创建input2.zip,内部nput.zip。 总

  • 问题内容: 我当前正在提取war文件的内容,然后将一些新文件添加到目录结构中,然后创建一个新的war文件。 所有这些都是通过Java以编程方式完成的-但我想知道,复制war文件然后追加文件是否会更有效-那么只要战争扩大,我就不必等待,再次被压缩。 但是,在文档或任何在线示例中,我似乎都找不到找到此方法的方法。 任何人都可以给一些提示或指示吗? 更新: 答案之一中提到的TrueZip似乎是一个非常好

  • 问题内容: 我们可以创建一个zip新文件并使用Go Language添加文件。 但是,如何使用GoLang使用现有的zip文件添加新文件? 如果可以使用Create函数,如何获取zip.writer参考? 有点困惑。 问题答案: 经过更多分析,我发现无法使用现有的zip文件添加任何文件。 但是,通过遵循此URL中提供的技巧,我能够使用tar文件添加文件。