Статьи

Определение типов файлов в Java

Программно определить тип файла может быть удивительно сложно, и было предложено и реализовано много подходов идентификации файлов на основе контента . Существует несколько реализаций, доступных в Java для обнаружения типов файлов, и большинство из них в значительной степени или исключительно основаны на расширениях файлов. В этом посте рассматриваются некоторые из наиболее распространенных реализаций обнаружения типов файлов в Java.

В этом посте демонстрируется несколько подходов к идентификации типов файлов в Java. Каждый подход кратко описан, проиллюстрирован листингом кода, а затем связан с выводом, который демонстрирует, как различные общие файлы печатаются на основе расширений. Некоторые из подходов являются настраиваемыми, но все показанные здесь примеры используют сопоставления «по умолчанию», как это предусмотрено «из коробки», если не указано иное.

О примерах

Снимки экрана, показанные в этом посте, представляют собой каждый из перечисленных фрагментов кода, запускаемых для определенных тематических файлов, созданных для тестирования различных реализаций обнаружения типов файлов в Java. Прежде чем рассказать об этих подходах и продемонстрировать тип, который обнаруживает каждый из них, я перечисляю тестируемые файлы, их имена и имена.

файл
название
файл
расширение
файл
Тип
Тип совпадений
Конвенция о продлении?
actualXml.xml XML XML да
blogPostPDF PDF нет
blogPost.pdf PDF PDF да
blogPost.gif GIF GIF да
blogPost.jpg JPG JPEG да
blogPost.png PNG PNG да
blogPostPDF.txt текст PDF нет
blogPostPDF.xml XML PDF нет
blogPostPNG.gif GIF PNG нет
blogPostPNG.jpg JPG PNG нет
dustin.txt текст Текст да
dustin.xml XML Текст нет
Дастин Текст нет

Files.probeContentType (Path) [JDK 7]

Java SE 7 представила высоко утилитарный класс Files, и Javadoc этого класса кратко описывает его использование: «Этот класс состоит исключительно из статических методов, которые работают с файлами, каталогами или другими типами файлов» и «в большинстве случаев с методами, определенными здесь делегирует соответствующему провайдеру файловой системы для выполнения файловых операций ».

Класс java.nio.file.Files предоставляет метод probeContentType (Path), который «проверяет тип содержимого файла» посредством использования «установленных реализаций FileTypeDetector» (Javadoc также отмечает, что «данный вызов виртуальной машины Java») ведет общесистемный список детекторов типов файлов »).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Identify file type of file with provided path and name
 * using JDK 7's Files.probeContentType(Path).
 *
 * @param fileName Name of file whose type is desired.
 * @return String representing identified type of file with provided name.
 */
public String identifyFileTypeUsingFilesProbeContentType(final String fileName)
{
   String fileType = "Undetermined";
   final File file = new File(fileName);
   try
   {
      fileType = Files.probeContentType(file.toPath());
   }
   catch (IOException ioException)
   {
      out.println(
           "ERROR: Unable to determine file type for " + fileName
              + " due to exception " + ioException);
   }
   return fileType;
}

Когда вышеуказанный подход на основе Files.probeContentType(Path) выполняется для набора файлов, определенных ранее, выходные данные отображаются так, как показано на следующем снимке экрана.

filesProbeContentTypeOnFiles

Снимок экрана показывает, что поведение по умолчанию для Files.probeContentType(Path) в моей JVM, похоже, тесно связано с расширением файла. Файлы без расширений показывают «ноль» для типа файла, а другие перечисленные типы файлов соответствуют расширениям файлов, а не их фактическому содержанию. Например, все три файла с именами, начинающимися с «dustin», на самом деле являются одним и тем же текстовым файлом, Files.probeContentType(Path) одного предложения, но Files.probeContentType(Path) утверждает, что каждый из них имеет различный тип, и перечисленные типы тесно связаны с различными расширениями файлов. по существу, тот же текстовый файл.

MimetypesFileTypeMap.getContentType (String) [JDK 6]

Класс MimetypesFileTypeMap был представлен в Java SE 6 для обеспечения «типизации данных файлов через их расширение файлов» с использованием «формата .mime.types ». Javadoc класса объясняет, где в данной системе класс ищет записи в файлах типов MIME. В моем примере используются те, которые поставляются из коробки с моей установкой JDK 8. Следующий листинг кода демонстрирует использование javax.activation.MimetypesFileTypeMap .

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * Identify file type of file with provided name using
 * JDK 6's MimetypesFileTypeMap.
 *
 * See Javadoc documentation for MimetypesFileTypeMap class
 * for details on how to configure mapping of file types or extensions.
 */
public String identifyFileTypeUsingMimetypesFileTypeMap(final String fileName)
{   
   final MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
   return fileTypeMap.getContentType(fileName);
}

Следующий снимок экрана демонстрирует результаты выполнения этого примера с набором тестовых файлов.

mimeTypesFileMapOnFiles

Эти выходные данные указывают, что подход MimetypesFileTypeMap возвращает MIME- тип application / octet-stream для нескольких файлов, включая файлы XML и текстовые файлы без суффикса .txt . Мы также видим, что, как и ранее рассмотренный подход, этот подход в некоторых случаях использует расширение файла для определения типа файла и поэтому неправильно сообщает фактический тип файла, когда этот тип отличается от того, что обычно подразумевается в его расширении.

URLConnection.getContentType ()

Я расскажу о трех методах в URLConnection, которые поддерживают обнаружение типов файлов. Первым является URLConnection.getContentType () , метод, который «возвращает значение поля заголовка content-type . Использование этого метода экземпляра продемонстрировано в следующем листинге кода, а результат выполнения этого кода для общих тестовых файлов показан после листинга кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Identify file type of file with provided path and name
 * using JDK's URLConnection.getContentType().
 *
 * @param fileName Name of file whose type is desired.
 * @return Type of file for which name was provided.
 */
public String identifyFileTypeUsingUrlConnectionGetContentType(final String fileName)
{
   String fileType = "Undetermined";
   try
   {
      final URL url = new URL("file://" + fileName);
      final URLConnection connection = url.openConnection();
      fileType = connection.getContentType();
   }
   catch (MalformedURLException badUrlEx)
   {
      out.println("ERROR: Bad URL - " + badUrlEx);
   }
   catch (IOException ioEx)
   {
      out.println("Cannot access URLConnection - " + ioEx);
   }
   return fileType;
}

urlConnectionContentTypeOnFiles

Подход обнаружения файлов с использованием URLConnection.getContentType() тесно связан с расширениями файлов, а не с фактическим типом файлов. Если расширение отсутствует, возвращается строка «content / unknown».

URLConnection.guessContentTypeFromName (String)

Второй подход к обнаружению файлов, предоставляемый URLConnection который я расскажу здесь, – это его метод guessContentTypeFromName (String) . Использование этого статического метода продемонстрировано в следующем листинге кода и соответствующем снимке экрана вывода.

01
02
03
04
05
06
07
08
09
10
11
/**
 * Identify file type of file with provided path and name
 * using JDK's URLConnection.guessContentTypeFromName(String).
 *
 * @param fileName Name of file whose type is desired.
 * @return Type of file for which name was provided.
 */
public String identifyFileTypeUsingUrlConnectionGuessContentTypeFromName(final String fileName)
{
   return URLConnection.guessContentTypeFromName(fileName);
}

urlConnectionGuessContentTypeFromNameOnFiles

guessContentTypeFromName(String) URLConnection для guessContentTypeFromName(String) обнаружения файлов « guessContentTypeFromName(String) показывает« ноль »для файлов без расширений файлов и в противном случае возвращает строковые представления типов файлов, которые близко отражают расширения файлов. Эти результаты очень похожи на результаты, представленные в Files.probeContentType(Path) показанном ранее, с одним заметным отличием, состоящим в том, что в guessContentTypeFromName(String) URLConnection идентифицирует файлы с расширением .xml как файлы типа «application / xml» в то время как Files.probeContentType(Path) идентифицирует эти типы файлов как «text / xml».

URLConnection.guessContentTypeFromStream (InputStream)

Третий подход, который я рассмотрю и который предоставляется URLConnection для определения типа файла, заключается в использовании статического метода класса guessContentTypeFromStream (InputStream) . Список кода, использующий этот подход, и связанный с ним вывод в снимке экрана показаны ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Identify file type of file with provided path and name
 * using JDK's URLConnection.guessContentTypeFromStream(InputStream).
 *
 * @param fileName Name of file whose type is desired.
 * @return Type of file for which name was provided.
 */
public String identifyFileTypeUsingUrlConnectionGuessContentTypeFromStream(final String fileName)
{
   String fileType;
   try
   {
      fileType = URLConnection.guessContentTypeFromStream(new FileInputStream(new File(fileName)));
   }
   catch (IOException ex)
   {
      out.println("ERROR: Unable to process file type for " + fileName + " - " + ex);
      fileType = "null";
   }
   return fileType;
}

urlConnectionGuessContentTypeFromInputStreamOnFilesAllNull

Все типы файлов являются нулевыми! Причина этого, по-видимому, объясняется Javadoc для параметра URLConnection.guessContentTypeFromStream(InputStream) метода URLConnection.guessContentTypeFromStream(InputStream) : «входной поток, поддерживающий метки». Оказывается, что экземпляры FileInputStream в моих примерах не поддерживают метки (их вызовы markSupported () все возвращают false ).

Апач Тика

Все примеры обнаружения файлов, описанные в этом посте, были подходами, предоставленными JDK. Существуют сторонние библиотеки, которые также можно использовать для обнаружения типов файлов в Java. Одним из примеров является Apache Tika , «инструментарий анализа контента», который «обнаруживает и извлекает метаданные и текст из более чем тысячи различных типов файлов». В этой статье я расскажу об использовании класса фасадов Tika и метода его обнаружения (String) для обнаружения типов файлов. Вызов метода экземпляра одинаков в трех примерах, которые я показываю, но результаты отличаются, потому что каждый экземпляр класса фасада Tika создается с различным детектором .

Создание экземпляров экземпляров Tika с различными Detector показано в следующем листинге кода.

1
2
3
4
5
6
7
8
/** Instance of Tika facade class with default configuration. */
private final Tika defaultTika = new Tika();
 
/** Instance of Tika facade class with MimeTypes detector. */
private final Tika mimeTika = new Tika(new MimeTypes());
his is
/** Instance of Tika facade class with Type detector. */
private final Tika typeTika = new Tika(new TypeDetector());

С этими тремя экземплярами Tika созданными на их соответствующих Detector , мы можем вызывать метод detect(String) в каждом экземпляре для набора тестовых файлов. Код для этого показан ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * Identify file type of file with provided name using
 * Tika's default configuration.
 *
 * @param fileName Name of file for which file type is desired.
 * @return Type of file for which file name was provided.
 */
public String identifyFileTypeUsingDefaultTika(final String fileName)
{
   return defaultTika.detect(fileName);
}
 
/**
 * Identify file type of file with provided name using
 * Tika's with a MimeTypes detector.
 *
 * @param fileName Name of file for which file type is desired.
 * @return Type of file for which file name was provided.
 */
public String identifyFileTypeUsingMimeTypesTika(final String fileName)
{
   return mimeTika.detect(fileName);
}
 
/**
 * Identify file type of file with provided name using
 * Tika's with a Types detector.
 *
 * @param fileName Name of file for which file type is desired.
 * @return Type of file for which file name was provided.
 */
public String identifyFileTypeUsingTypeDetectorTika(final String fileName)
{
   return typeTika.detect(fileName);
}

Когда три приведенных выше примера обнаружения Tika выполняются для одного и того же набора файлов, которые использовались в предыдущих примерах, выходные данные отображаются так, как показано на следующем снимке экрана.

tikaThreeDetectorsFileTypeDetectionOnFiles

Из результатов видно, что детектор Tika по умолчанию сообщает о типах файлов аналогично некоторым другим подходам, показанным ранее в этом посте (очень тесно связанным с расширением файла). Два других продемонстрированных детектора утверждают, что тип файла в большинстве случаев является application / octet-stream. Поскольку я вызвал перегруженную версию detect(-) которая принимает строку, обнаружение типа файла «основано на известных расширениях имени файла».

Если вместо detect(String) используется перегруженный метод обнаружения (File) , результаты идентифицированного типа файла будут намного лучше, чем в предыдущих примерах Tika и предыдущих примерах JDK. На самом деле, «поддельные» расширения не так обманывают детекторы, и детектор Tika по умолчанию особенно хорош в моих примерах для определения подходящего типа файла, даже если расширение не является нормальным, ассоциированным с этим типом файла. Код для использования Tika.detect(File) и связанный с ним вывод показан ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
    * Identify file type of file with provided name using
    * Tika's default configuration.
    *
    * @param fileName Name of file for which file type is desired.
    * @return Type of file for which file name was provided.
    */
   public String identifyFileTypeUsingDefaultTikaForFile(final String fileName)
   {
      String fileType;
      try
      {
         final File file = new File(fileName);
         fileType = defaultTika.detect(file);
      }
      catch (IOException ioEx)
      {
         out.println("Unable to detect type of file " + fileName + " - " + ioEx);
         fileType = "Unknown";
      }
      return fileType;
   }
 
   /**
    * Identify file type of file with provided name using
    * Tika's with a MimeTypes detector.
    *
    * @param fileName Name of file for which file type is desired.
    * @return Type of file for which file name was provided.
    */
   public String identifyFileTypeUsingMimeTypesTikaForFile(final String fileName)
   {
      String fileType;
      try
      {
         final File file = new File(fileName);
         fileType = mimeTika.detect(file);
      }
      catch (IOException ioEx)
      {
         out.println("Unable to detect type of file " + fileName + " - " + ioEx);
         fileType = "Unknown";
      }
      return fileType;
   }
 
   /**
    * Identify file type of file with provided name using
    * Tika's with a Types detector.
    *
    * @param fileName Name of file for which file type is desired.
    * @return Type of file for which file name was provided.
    */
   public String identifyFileTypeUsingTypeDetectorTikaForFile(final String fileName)
   {
      String fileType;
      try
      {
         final File file = new File(fileName);
         fileType = typeTika.detect(file);
      }
      catch (IOException ioEx)
      {
         out.println("Unable to detect type of file " + fileName + " - " + ioEx);
         fileType = "Unknown";
      }
      return fileType;
   }

tikaThreeDetectorsFileArgFileTypeDetectionOnFiles

Предостережения и настройка

Обнаружение типа файла не является простым делом. Подходы Java для обнаружения файлов, продемонстрированные в этом посте, предоставляют базовые подходы к обнаружению файлов, которые часто сильно зависят от расширения имени файла. Если файлы имеют имена с обычными расширениями, которые распознаются методом обнаружения файлов, таких подходов обычно достаточно. Однако если используются нетрадиционные расширения типов файлов или они предназначены для файлов с типами, отличными от тех, которые обычно связаны с этим расширением, большинство из этих подходов к обнаружению файлов выходят из строя без настройки. К счастью, большинство из этих подходов предоставляют возможность настраивать сопоставление расширений файлов с типами файлов. Подход Tika с использованием Tika.detect(File) как правило, был наиболее точным в примерах, показанных в этом посте, когда расширения не были стандартными для определенных типов файлов.

Вывод

Существует множество механизмов для простого обнаружения типов файлов в Java. В этой статье были рассмотрены некоторые стандартные подходы JDK для обнаружения файлов и некоторые примеры использования Tika для обнаружения файлов.

Ссылка: Определение типов файлов в Java от нашего партнера JCG Дастина Маркса в блоге Inspired by Actual Events .