2015-04-26 13 views
6

Dla this package, jednym z moich kolejnych kroków jest napisanie serii FileTypeDetector, aby metoda była domyślnie inteligentniejsza niż domyślna (domyślny dostarczony typ pliku opiera się tylko na "rozszerzeniach nazw plików").Jak napisać FileTypeDetector dla archiwów zip?

Jak wspomina javadoc wspomnianej powyżej metody, ta metoda polega na wystąpieniu instancji FileTypeDetector s zadeklarowanych w pliku.

ja najpierw testowane za pomocą prostego dostawcy wykryć pliki PNG za pomocą nagłówka pliku:

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; 
    } 
} 

To działa. Teraz, po szybkim spojrzeniu na API, myślałem, że będzie to dobry sposób, aby wykryć, czy plik był błyskawiczny:

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; 
     } 
    } 
} 

Zawartość META-INF/services/java.nio.file.spi.FileTypeDetector było to:

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

Z aktualne testy, zadziałało; dla zipu stworzyłem pusty plik zip, dla testu PNG użyłem this image.

Pełny Test:

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(); 
    } 
} 

Jednak ...

Gdybym odwrócić listę wdrożeń w pliku services, czyli plik teraz jest:

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

wówczas Plik PNG jest wykrywany jako plik zip!

Po pewnym debugowania zauważyłem, że:

  • otwarciu PNG jako ZipInputStream nie zawiódł ...
  • ... i .getNextEntry() powrócił zerowa!

bym oczekiwać przynajmniej.getNextEntry() rzucać ZipException.

Dlaczego nie? Jak wykryć niezawodnie, czy plik jest zip?

Dalsza uwaga: dotyczy to Path s; w związku z tym wszystko, co jest File, jest bezużyteczne.

Odpowiedz

0

Dlaczego nie?

Cóż, JavaDoc dla getNextEntry() mówi, że ZipException lub IOException występuje,

jeśli błąd pliku ZIP doszło

jeśli wystąpił błąd I/O

odpowiednio.

Na podstawie tej cudownie pomocnej informacji (kaszel) nie możemy przyjąć żadnych założeń, że spowoduje ona zgłoszenie wyjątku w przypadku napotkania nieprawidłowego wpisu.

Jak mogę niezawodnie wykryć, czy plik jest zip?

Specyfikację formatu pliku ZIP, który pierwotnie był PKZip, można znaleźć pod numerem here. Choć wszystko dobrze się czyta :), spójrz na sekcję 4; 4.3.16 w szczególności. Określa "End of central directory record", które mają wszystkie pliki ZIP (nawet puste).

+1

Nie jest to jednak pusty plik zip jako nagłówek. – fge

+0

@fge Masz rację, zaktualizowałem swoją odpowiedź. – pathfinderelite