【问题标题】:How to accurately determine mime data from a file?如何准确地确定文件中的 mime 数据?
【发布时间】:2012-01-19 06:48:32
【问题描述】:

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

方法一:

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

方法二:

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

方法三:

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 文件,但是除了第一种方法我认为是因为该方法使用的内置 FileTypeMap 中的文件类型数量有限。

我环顾四周,有人说这是因为在文件中检测到偏移量的方式,因此内容类型被错误地拾取,正如wiki on detection file types in PHP 中所指出的那样.不幸的是,wiki 然后继续使用扩展名来确定文件类型,这不是我想要做的,因为它不可靠。

谁能给我指出正确的方向,让我在 Java 中正确检测文件类型的方法?

干杯, 阿列克谢蓝。

编辑:正如@IronMensan 在下面的评论中所说,似乎没有具体的解决方案。我确实发现这个非常有趣的research paper 以几种方式应用机器学习来帮助解决这个问题,但似乎没有一个完整的证明答案。我认为我最好的选择是尝试将文件传递给 excel 文件阅读器并捕获任何不正确的格式异常。

【问题讨论】:

  • 世界上的文件类型繁多,没有完美的解决方案,问题归根结底是一个基于文件内容的猜谜游戏。有些方法会比其他方法更好。
  • 嗨 IronMensan,感谢您的评论,知道为什么在检查 Excel 文件上的 MIME 类型时返回为 msword 类型吗?认为现在这将是一个很好识别的类型,对我来说 Excel 文件将是最重要的...... :) 再次干杯
  • file 命令是否为您的样本返回正确的结果?它带有一个库libmagic,尽管我猜你的一个尝试以某种方式使用了它,或者它的派生词。尽管如此,它仍然是事实上的标准解决方案。至于 Word 错误检测,我猜识别器实际上找到了顶级容器,这对于几种 Office 文件格式是相同的。
  • file 命令只是说它是一个 Microsoft Office 文档,这是朝着正确方向迈出的一步,但对我的需要还不够具体。我一直在环顾四周,似乎这是一个涉及特征选择的活跃研究领域,因为没有针对 MIME 类型的特定标准。我确实发现这篇研究论文 [alphaminers.net/thesis/International%20Conference/… 可能会有所帮助,但它使我认为是一个简单的问题更难实现。
  • 请注意,有更多 MimeDetector 可用于 Mime-Utils:stackoverflow.com/a/13826438/2413303

标签: java mime-types file-type


【解决方案1】:

到目前为止,我找到的用于确定文件 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-parsers JAR。如果您没有获得 tika-parsers,您将不会获得任何异常,您将无法准确获得 MIME 类型,因此包含它非常重要。

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

【讨论】:

    【解决方案2】:

    正如 cmets 中所提到的,因为有很多可能的文件类型,所有可能的文件都可能会被命中和丢失,但您可能知道您通常要处理的文件类型。这个excellent list of magic numbers 帮助我最近围绕您提到的特定办公格式进行了检测(搜索 Microsoft Office),您会看到 MS Office 文件类型有一个指定的子类型(在文件的更深处)并让您具体确定您拥有哪种类型的文件。许多新格式(如 ODT、DOCX、OOXML 等)使用 ZIP 文件来保存它们的数据,因此您可能需要先检测 zip,然后再查找细节。

    【讨论】:

    • 我通过从偏移量 512 读取 8 个字节,然后将它们与常量进行比较,将其实现为一种解决方法,但效果很好。 :) 谢谢乔维润
    【解决方案3】:

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

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

    【讨论】:

    • 如果文件扩展名不同,例如:我有redirect.mappings 这是*.properties 文件,那么你上面的代码得到null
    • @ToKra 老实说,通常我想按照stackoverflow.com/questions/13775494/… 使用MimeUtils,但它有一个非常极端的依赖性,整个 sfl4j 记录器,它只是没有在我需要的 Android 上工作 - 当时我不想把它撕掉。
    • 还是这样。我尝试将 MimeUtils 与 spring boot 1.2.7 一起使用,并且 log4j 依赖项与 SB 的 logback 依赖项发生冲突。我试图从 MimeUtils 中排除 log4j,但它没有编译。
    猜你喜欢
    • 2012-01-25
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 2016-05-23
    • 2013-02-09
    • 1970-01-01
    • 2012-09-04
    • 2019-02-24
    相关资源
    最近更新 更多