如何为zip存档编写FileTypeDetector?

时间:2015-04-26 16:20:15

标签: java file-io zip mime-types jsr203

对于this package,我接下来的步骤之一是编写一系列FileTypeDetector以使方法Files.probeContentType()比默认情况下更智能(默认提供的文件类型检测器)依赖于"文件扩展名"仅限)。

正如前面提到的方法的javadoc所提到的,这个方法依赖于在FileTypeDetector文件中声明META-INF/services的实例。

我首先使用简单的提供程序进行测试,以使用文件头检测PNG文件:

public final class PngFileTypeDetector
    extends FileTypeDetector
{
    private static final byte[] PNG_HEADER = {
        (byte) 0x89,
        (byte) 0x50, (byte) 0x4E, (byte) 0x47,
        (byte) 0x0D, (byte) 0x0A,
        (byte) 0x1A,
        (byte) 0x0A
    };

    private static final int PNG_HEADER_SIZE = PNG_HEADER.length;

    @Override
    public String probeContentType(final Path path)
        throws IOException
    {
        final byte[] buf = new byte[PNG_HEADER_SIZE];

        try (
            final InputStream in = Files.newInputStream(path);
        ) {
            if (in.read(buf) != PNG_HEADER_SIZE)
                return null;
        }

        return Arrays.equals(buf, PNG_HEADER) ? "image/png" : null;
    }
}

有效。现在,在快速浏览一下API之后,我认为这是检测文件是否为zip的好方法:

public final class ZipFileTypeDetector
    extends FileTypeDetector
{
    @Override
    public String probeContentType(final Path path)
        throws IOException
    {
        // Rely on what the JDK has to offer...
        try (
            final InputStream in = Files.newInputStream(path);
            final ZipInputStream z = new ZipInputStream(in);
        ) {
            z.getNextEntry();
            return "application/zip";
        } catch (ZipException ignored) {
            return null;
        }
    }
}

META-INF/services/java.nio.file.spi.FileTypeDetector的内容是:

com.github.fge.filesystem.ftd.PngFileTypeDetector
com.github.fge.filesystem.ftd.ZipFileTypeDetector

通过当前的测试,它起作用了;对于zip我创建了一个空的zip文件,对于我使用this image的PNG测试。

完整测试:

public final class FileTypeDetectorTest
{
    private FileSystem fs;
    private Path path;

    @BeforeMethod
    public void initfs()
        throws IOException
    {
        fs = MemoryFileSystemBuilder.newLinux().build("testfs");
        path = fs.getPath("/foo");
    }

    @DataProvider
    public Iterator<Object[]> samples()
    {
        final List<Object[]> list = new ArrayList<>();

        String resourcePath;
        String mimeType;

        resourcePath = "/ftd/sample.png";
        mimeType = "image/png";
        list.add(new Object[] { resourcePath, mimeType });

        resourcePath = "/ftd/sample.zip";
        mimeType = "application/zip";
        list.add(new Object[] { resourcePath, mimeType });

        return list.iterator();
    }

    @Test(dataProvider = "samples")
    public void fileTypeDetectionTest(final String resourcePath,
        final String mimeType)
        throws IOException
    {
        @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
        final InputStream in
            = FileTypeDetectorTest.class.getResourceAsStream(resourcePath);

        if (in == null)
            throw new IOException(resourcePath + " not found in classpath");

        try (
            final InputStream inref = in;
        ) {
            Files.copy(inref, path);
        }

        assertThat(Files.probeContentType(path)).isEqualTo(mimeType);
    }

    @AfterMethod
    public void closefs()
        throws IOException
    {
        fs.close();
    }
}

...然而

如果我反转services文件中的实现列表,那么现在的文件是:

com.github.fge.filesystem.ftd.ZipFileTypeDetector
com.github.fge.filesystem.ftd.PngFileTypeDetector

然后PNG文件被检测为zip文件!

经过一些调试后,我注意到:

  • 打开PNG作为ZipInputStream并没有失败......
  • ... .getNextEntry()返回null!

我预计至少 .getNextEntry()会抛出ZipException

为什么没有?如何可靠地检测文件是否为zip?

进一步说明:这是Path s;因此任何File都无法使用。

1 个答案:

答案 0 :(得分:0)

  

为什么没有?

嗯,getNextEntry()的JavaDoc表示发生了ZipExceptionIOException

  

如果发生ZIP文件错误

     

如果发生I / O错误

分别

根据这些非常有用的信息( cough ),我们无法做出任何假设,即如果遇到无效条目,它会抛出异常。

  

如何可靠地检测文件是否为zip?

可以找到最初为PKZip的ZIP文件格式规范here。虽然它读得很好:),看看第4节;特别是4.3.16。它指定&#34;中心目录记录的结尾&#34;,所有ZIP文件都有(甚至是空文件)。