问题描述
我有一个包含多个 gzip 文件的 zip 存档。但是 gzip 文件的扩展名也是 .zip
。我使用 ZipInputStream 浏览 zip 存档。如何通过读取其内容而不是扩展来检测内部文件的类型。我也不需要更改(或重置)ZipInputStream 位置。
所以我需要;
- 使用 inputStream 读取 zip 中的文件(在我的例子中为 ZipInputStream)因为 zip 中的 zip 是可能的。
- 从内容中查找文件类型。
- 从内容中查找文件类型时,inputStream 位置不应改变。因为我会继续阅读下一个文件。
示例:
root/1.zip/2.zip/3.zip(其实3就是gzip)/4.txt
示例 Java 代码:
public static void main(String[] args) {
//root/1.zip/2.zip/3.zip(actually 3 is gzip)/4.txt
String file = "root/1.zip";
File rootZip = new File(file);
try (FileInputStream fis = new FileInputStream(rootZip)) {
lookupInZip(fis)
.stream()
.forEach(System.out::println);
} catch (IOException e) {
System.out.println("Failed to get files");
}
}
public static List<String> lookupInZip(InputStream inputStream) throws IOException {
Tika tika = new Tika();
List<String> paths = new ArrayList<>();
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry entry = zipInputStream.getNextEntry();
while (entry != null) {
String entryName = entry.getName();
if (!entry.isDirectory()) {
//Option 1
//String fileType = tika.detect(entryName);
//Option 2
String fileType = tika.detect(zipInputStream);
if ("application/zip".equals(fileType)) {
List<String> innerPaths = lookupInZip(zipInputStream);
paths.addAll(innerPaths);
} else {
paths.add(entryName);
}
}
entry = zipInputStream.getNextEntry();
}
return paths;
}
如果我使用选项 1,'3.zip' 被评估为 zip 文件,但它是 gzip。 如果我使用选项 2,'2.zip' 将通过使用其内容正确评估为 zip。但是当以递归方式为 '3.zip' 调用 lookupInZip() 时, zipInputStream.getNextEntry() 返回 null。因为在上一步中,我们使用 inputStream content 来检测类型和 inputStrem 位置的变化。
注意:tika.detect() 在实现中使用 BufferedInputStream 来重置 inputStream 位置,但它并没有解决我的问题。
解决方法
前两个字节足以判断它是否可能是 zip 文件、gzip 文件或其他内容。
如果前两个字节是 0x50 0x4b
,那么它很可能是一个 zip 文件。如果前两个字节是 0x1f 0x8b
,那么它很可能是一个 gzip 文件。如果两者都不是,那么文件就是别的东西。
匹配的前两个字节并不能保证它是那种类型,但从您的结构看来,它通常是一种或另一种,您可以使用扩展名作为进一步证实它被压缩的证据。
至于不改变位置,你需要一种方法可以在不推进位置的情况下窥视前两个字节,或者一种方法来获取它们然后取消它们以将位置返回到原来的位置。