如何准确地确定文件中的mime数据?

我正在为程序添加一些function,以便通过读取MIME数据来准确确定文件类型。 我已经尝试过几种方法:

方法1:

javax.activation.FileDataSource FileDataSource ds = new FileDataSource("~\\Downloads\\777135_new.xls"); String contentType = ds.getContentType(); System.out.println("The MIME type of the file is: " + contentType); //output = The MIME type of the file is: application/octet-stream 

方法2:

 import net.sf.jmimemagic.*; try { RandomAccessFile f = new RandomAccessFile("~\\Downloads\\777135_new.xls", "r"); byte[] fileBytes = new byte[(int)f.length()]; f.read(fileBytes); MagicMatch match = Magic.getMagicMatch(fileBytes); System.out.println("The Mime type is: " + match.getMimeType()); } catch(Exception e) { System.out.println(e); } //output = The Mime type is: application/msword 

方法3:

 import eu.medsea.mimeutil.*; MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector"); File f = new File ("~\\Downloads\\777135_new.xls"); Collection mimeTypes = MimeUtil.getMimeTypes(f); String mimeType = MimeUtil.getFirstMimeType(mimeTypes.toString()).toString(); String subMimeType = MimeUtil.getSubType(mimeTypes.toString()); System.out.println("The Mime type is: " + mimeTypes + ", " + mimeType + ", " + subMimeType); //output = The Mime type is: application/msword, application/msword, msword 

我在http://www.rgagnon.com/javadetails/java-0487.html找到了这三种方法。 但是我的问题是我正在测试这些方法的文件是我创建的文件,因此我知道它是一个Excel文件,但是仍然所有三种方法都错误地将类型作为msword提取,除了第一种方法我认为是因为该方法使用的内置FileTypeMap中的文件类型数量有限。

我已经浏览了一下,有些人说这是因为在文件中检测到偏移的方式,因此内容类型被错误地拾取,正如本维基中在PHP中检测文件类型所指出的那样。 不幸的是,维基然后继续使用扩展来确定文件类型,这不是我想要做的,因为它不可靠。

任何人都可以指出我正确的方向,以便在Java中正确检测文件类型吗?

干杯,阿列克谢蓝。

编辑:看起来没有特定的解决方案,因为@IronMensan在下面的评论中说。 我确实找到了这篇非常有趣的研究论文 ,它以一些方式应用机器学习来帮助解决这个问题,但似乎没有一个完整的certificate答案。 我认为我最好的选择是尝试将文件传递给excel文件阅读器并捕获任何不正确的格式exception。

正如评论中提到的那样,因为有很多可能的文件类型,所有可能的文件都会被命中和遗漏,但是你可能知道你通常要处理的文件类型。 这个优秀的魔术数字列表帮助我最近围绕您提到的特定办公室格式(搜索Microsoft Office)进行检测,您将看到MS office文件类型指定了一个子类型(进一步进入文件)并让您具体了解您拥有哪种类型的文件。 许多新格式(如ODT,DOCX,OOXML等)使用ZIP文件来保存其数据,因此您可能需要首先检测zip,然后查找具体信息。

到目前为止,我发现确定文件MIME类型的最准确的工具是Apache Tika 。 这是我目前使用的略有修改(使用Tika版本1.0)

 import org.apache.tika.detect.DefaultDetector; import org.apache.tika.detect.Detector; import org.apache.tika.io.TikaInputStream; import org.apache.tika.metadata.Metadata; import org.apache.tika.mime.MimeTypes; private static final Detector DETECTOR = new DefaultDetector( MimeTypes.getDefaultMimeTypes()); public static String detectMimeType(final File file) throws IOException { TikaInputStream tikaIS = null; try { tikaIS = TikaInputStream.get(file); /* * You might not want to provide the file's name. If you provide an Excel * document with a .xls extension, it will get it correct right away; but * if you provide an Excel document with .doc extension, it will guess it * to be a Word document */ final Metadata metadata = new Metadata(); // metadata.set(Metadata.RESOURCE_NAME_KEY, file.getName()); return DETECTOR.detect(tikaIS, metadata).toString(); } finally { if (tikaIS != null) { tikaIS.close(); } } } 

由于Tika将使用魔术数字,但在不确定时也会查看文件的内容,这个过程可能需要一点时间(我的PC需要3.268秒来检查15个文件)。

另外,不要犯同样的错误。 如果你得到tika-core JAR,你也应该得到tika解析器 JAR。 如果你没有获得tika解析器,你将不会得到任何例外,你将无法准确地获得MIME类型,因此包含它是非常重要的。

另一种方法是获取tika-app JAR,它包含tika-coretika-parsers和所有依赖项(它们很多:poi,poi-ooxml,xmlbeans,commons-compress,仅举几例)。

我不完全确定它有多准确,但在简单的情况下这对我有用。

  FileNameMap fileNameMap = URLConnection.getFileNameMap(); String type = fileNameMap.getContentTypeFor(filePath);