操作步骤:
- 使用任意压缩工具创建一个测试用Zip压缩文件;
使用如下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(); }
- 使用Notepad++打开上面的zip文件,在文件头和尾分别添加任意长度的文字,保存。
- 再次执行上面代码,仍然能正确识别压缩文件内容。
但是换用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