如何从 zip 存档中的内容检测文件类型?

问题描述

我有一个包含多个 gzip 文件的 zip 存档。但是 gzip 文件的扩展名也是 .zip 。我使用 ZipInputStream 浏览 zip 存档。如何通过读取其内容而不是扩展来检测内部文件的类型。我也不需要更改(或重置)ZipInputStream 位置。

所以我需要;

示例:

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 文件。如果两者都不是,那么文件就是别的东西。

匹配的前两个字节并不能保证它是那种类型,但从您的结构看来,它通常是一种或另一种,您可以使用扩展名作为进一步证实它被压缩的证据。

至于不改变位置,你需要一种方法可以在不推进位置的情况下窥视前两个字节,或者一种方法来获取它们然后取消它们以将位置返回到原来的位置。