以编程方式确定文件的类型可能非常棘手,并且已经提出并实现了许多基于内容的文件标识方法。 Java中有几种可用于检测文件类型的实现,其中大多数很大程度上或完全基于文件的扩展名。 这篇文章介绍了Java中最常见的文件类型检测实现。
本文介绍了几种在Java中识别文件类型的方法。 简要描述每种方法,并用代码清单进行说明,然后将其与输出相关联,该输出演示如何根据扩展名键入不同的公用文件。 某些方法是可配置的,但是除非另有说明,否则此处显示的所有示例均使用开箱即用提供的“默认”映射。
关于示例
这篇文章中显示的屏幕快照是针对某些主题文件运行的每个列出的代码片段,这些主题文件是为了测试Java中文件类型检测的不同实现而创建的。 在介绍这些方法并演示每种方法检测到的类型之前,我将列出要测试的文件,它们的名称以及它们的真实名称。
文件 名称 | 文件 延期 | 文件 类型 | 类型匹配 延期公约? |
---|---|---|---|
ActualXml.xml | XML文件 | XML格式 | 是 |
博客文章PDF | 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 | 没有 |
ustin.txt | 文本文件 | 文本 | 是 |
ustin.xml | XML文件 | 文本 | 没有 |
尘土 | 文本 | 没有 |
Files.probeContentType(Path)[JDK 7]
Java SE 7引入了高度实用的Files类 , 该类的Javadoc简洁地描述了它的用法:“该类专门由对文件,目录或其他类型的文件进行操作的静态方法组成”,以及“在大多数情况下,此处定义的方法”将委派给关联的文件系统提供者以执行文件操作。”
java.nio.file.Files
类提供了方法probeContentType(Path),该方法通过使用“已安装的FileTypeDetector实现”来“探测文件的内容类型”(Javadoc还指出,“ Java虚拟机的给定调用”维护系统范围的文件类型检测器列表”)。
/*** 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)
的方法时,输出将显示,如下一个屏幕快照所示。
屏幕快照表明我的JVM上Files.probeContentType(Path)
的默认行为似乎与文件扩展名紧密相关。 没有扩展名的文件的文件类型显示为“ null”,其他列出的文件类型与文件的扩展名而不是其实际内容匹配。 例如,所有三个名称都以“ dustin”开头的文件实际上都是同一个单句文本文件,但是Files.probeContentType(Path)
声明它们都是不同的类型,并且列出的类型与不同的文件扩展名紧密相关。基本上是相同的文本文件。
MimetypesFileTypeMap.getContentType(String)[JDK 6]
Java SE 6引入了 MimetypesFileTypeMap类,以使用“ .mime.types
格式”提供“通过文件扩展名进行文件的数据键入”。 该类的Javadoc解释了该类在给定系统中的哪里查找MIME类型文件条目。 我的示例使用JDK 8安装中提供的现成产品。 下一个代码清单演示了javax.activation.MimetypesFileTypeMap
用法。
/*** Identify file type of file with provided name using* JDK 6's MimetypesFileTypeMap.** See Javadoc documentation for MimetypesFileTypeMap class* (http://docs.oracle.com/javase/8/docs/api/javax/activation/MimetypesFileTypeMap.html)* 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);
}
下一个屏幕快照展示了针对一组测试文件运行此示例的输出。
此输出表明MimetypesFileTypeMap
方法为多个文件(包括XML文件和不带.txt
后缀的文本文件)返回application / octet-stream的MIME类型。 我们还看到,就像前面讨论的方法一样,这种方法在某些情况下使用文件的扩展名来确定文件类型,因此当该文件名与常规扩展名不同时,会错误地报告该文件的实际文件类型。
URLConnection.getContentType()
我将介绍URLConnection中支持文件类型检测的三种方法。 第一个是URLConnection.getContentType() ,该方法“返回content-type
标头字段的值”。 下一个代码清单中演示了此实例方法的使用,并且在代码清单后显示了针对通用测试文件运行该代码的输出。
/*** 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;
}
使用URLConnection.getContentType()
的文件检测方法与文件的扩展名(而不是实际的文件类型URLConnection.getContentType()
高度相关。 如果没有扩展名,则返回的字符串为“ content / unknown”。
URLConnection.guessContentTypeFromName(String)
我将在这里介绍的URLConnection
提供的第二种文件检测方法是其方法guessContentTypeFromName(String) 。 下一个代码清单和相关的输出屏幕快照中演示了此静态方法的使用。
/*** 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);
}
URLConnection
的guessContentTypeFromName(String)
文件检测方法对没有文件扩展名的文件显示为“ null”,否则返回与文件扩展名紧密镜像的文件类型字符串表示形式。 这些结果与前面显示的Files.probeContentType(Path)
方法提供的结果非常相似,一个显着的区别是URLConnection
的guessContentTypeFromName(String)
方法将扩展名为.xml
的文件标识为文件类型“ application / xml”而Files.probeContentType(Path)
将这些相同的文件类型标识为“ text / xml”。
URLConnection.guessContentTypeFromStream(InputStream)
我介绍的URLConnection
为文件类型检测提供的第三种方法是通过类的静态方法guessContentTypeFromStream(InputStream) 。 接下来显示使用此方法的代码清单以及屏幕快照中的相关输出。
/*** 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;
}
所有文件类型均为空! Javadoc似乎对URLConnection.guessContentTypeFromStream(InputStream)
方法的InputStream参数进行了解释:“支持标记的输入流。” 事实证明,在我的示例中, FileInputStream的实例不支持标记(它们对markSupported()的调用均返回false
)。
阿帕奇·蒂卡(Apache Tika)
到目前为止,本文中涵盖的所有文件检测示例都是JDK提供的方法。 有些第三方库也可用于检测Java中的文件类型。 一个示例是Apache Tika ,它是一种“内容分析工具包”,它“可以从上千种不同的文件类型中检测并提取元数据和文本。” 在本文中,我将研究如何使用Tika的Facade类及其detect(String)方法来检测文件类型。 我展示的三个示例中的实例方法调用相同,但是结果不同,因为Tika
Facade类的每个实例都使用不同的Detector实例化。
下代码清单显示了具有不同Detector
的Tika
实例的实例化。
/** 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());
通过用各自的Detector
实例化Tika
这三个实例,我们可以在每个实例上为测试文件集调用detect(String)
方法。 接下来显示此代码。
/*** 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检测示例时,输出将显示,如下一个屏幕快照所示。
从输出中我们可以看到,默认的Tika检测器报告的文件类型类似于本文前面显示的其他一些方法(与文件扩展名紧密相关)。 其他两个演示过的检测器指出,在大多数情况下,文件类型为application / octet-stream。 因为我调用了接受String的detect(-)
的重载版本,所以文件类型检测是“基于已知的文件扩展名”。
如果使用重载的detect(File)方法而不是detect(String)
,则标识的文件类型结果将比以前的Tika示例和以前的JDK示例好得多。 实际上,“假”扩展名不会使检测器蒙上阴影,在我的示例中,默认的Tika检测器在识别适当的文件类型方面尤其出色,即使该扩展名不是与该文件类型关联的普通文件类型也是如此。 接下来显示使用Tika.detect(File)
的代码和关联的输出。
/*** 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;}
注意事项和自定义
文件类型检测并不是一件容易的事。 本文中演示的Java文件检测方法提供了文件检测的基本方法,这些方法通常高度依赖于文件名的扩展名。 如果使用文件检测方法可以识别的常规扩展名来命名文件,则这些方法通常就足够了。 但是,如果使用非常规文件类型扩展名,或者该扩展名用于具有与该扩展名常规相关联的文件之外的其他类型的文件,则这些文件检测方法中的大多数会在没有定制的情况下崩溃。 幸运的是,大多数方法都提供了自定义文件扩展名到文件类型的映射的能力。 在本文显示的示例中,当扩展名不是特定文件类型的常规扩展名时,使用Tika.detect(File)
的Tika方法通常是最准确的。
结论
在Java中,有许多机制可用于简单文件类型检测。 这篇文章回顾了一些用于文件检测的标准JDK方法,以及一些使用Tika进行文件检测的示例。
翻译自: https://www.javacodegeeks.com/2015/02/determining-file-types-java.html