使用 Apache tika 获取 MimeType 子类型

Posted

技术标签:

【中文标题】使用 Apache tika 获取 MimeType 子类型【英文标题】:Getting MimeType subtype with Apache tika 【发布时间】:2011-10-31 13:05:41 【问题描述】:

对于 odt、ppt、pptx、xlsx 等文档,我需要获取 iana.org MediaType 而不是 application/zip 或 application/x-tika-msoffice。

如果您查看 mimetypes.xml,就会发现 mimeType 元素由 iana.org mime-type 和“sub-class-of”组成

   <mime-type type="application/msword">
    <alias type="application/vnd.ms-word"/>
    ............................
    <glob pattern="*.doc"/>
    <glob pattern="*.dot"/>
    <sub-class-of type="application/x-tika-msoffice"/>
  </mime-type>

如何获取 iana.org mime-type 名称而不是父类型名称?

在测试 mime 类型检测时,我会这样做:

MediaType mediaType = MediaType.parse(tika.detect(inputStream));
String mimeType = mediaType.getSubtype();

测试结果:

FAILED: getsCorrectContentType("application/vnd.ms-excel", docs/xls/en.xls)
java.lang.AssertionError: expected:<application/vnd.ms-excel> but was:<x-tika-msoffice>

FAILED: getsCorrectContentType("vnd.openxmlformats-officedocument.spreadsheetml.sheet", docs/xlsx/en.xlsx)
java.lang.AssertionError: expected:<vnd.openxmlformats-officedocument.spreadsheetml.sheet> but was:<zip>

FAILED: getsCorrectContentType("application/msword", doc/en.doc)
java.lang.AssertionError: expected:<application/msword> but was:<x-tika-msoffice>

FAILED: getsCorrectContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document", docs/docx/en.docx)
java.lang.AssertionError: expected:<application/vnd.openxmlformats-officedocument.wordprocessingml.document> but was:<zip>

FAILED: getsCorrectContentType("vnd.ms-powerpoint", docs/ppt/en.ppt)
java.lang.AssertionError: expected:<vnd.ms-powerpoint> but was:<x-tika-msoffice>

有没有办法从 mimetypes.xml 获取实际的子类型?而不是 x-tika-msoffice 或 application/zip ?

此外,我从来没有得到 application/x-tika-ooxml,而是 xlsx、docx、pptx 文档的 application/zip。

【问题讨论】:

【参考方案1】:

最初,Tika 仅支持通过 Mime Magic 或文件扩展名 (glob) 进行检测,因为这是 Tika 之前的所有大多数 mime 检测。

由于在检测容器格式时 Mime Magic 和 glob 存在问题,因此决定在 Tika 中添加一些新的检测器来处理这些问题。 Container Aware Detectors 获取整个文件,打开并处理容器,然后根据内容计算出确切的文件类型。最初,您需要显式调用它们,但随后它们被包裹在 ContainerAwareDetector 中,您将在一些答案中看到。

从那时起,Tika 添加了一个服务加载器模式,最初是针对 Parsers 的。这允许类在存在时自动加载,并以一种通用的方式来识别哪些是合适的并使用它们。这种支持随后也扩展到了检测器,此时可以删除旧的 ContainerAwareDetector 以支持更清洁的东西。

如果您使用的是 Tika 1.2 或更高版本,并且想要准确检测所有格式,包括容器格式,您需要执行以下操作:

 TikaConfig config = TikaConfig.getDefaultConfig();
 Detector detector = config.getDetector();

 TikaInputStream stream = TikaInputStream.get(fileOrStream);

 Metadata metadata = new Metadata();
 metadata.add(Metadata.RESOURCE_NAME_KEY, filenameWithExtension);
 MediaType mediaType = detector.detect(stream, metadata);

如果您仅使用 Core Tika jar (tika-core-1.2-....) 运行此程序,那么唯一存在的检测器将是 mime 魔法检测器,您将获得基于魔法的旧式检测+ 仅限全局。但是,如果您同时使用 Core 和 Parser Tika jars(加上它们的依赖项),或者从 Tika App(自动包括核心 + 解析器 + 依赖项)运行它,那么 DefaultDetector 将使用所有各种不同的容器检测器来处理您的文件.如果您的文件是基于 zip 的,则检测将包括处理 zip 结构以根据其中的内容识别文件类型。这将为您提供所需的高精度检测,而无需依次调用许多不同的解析器。 DefaultDetector 将使用所有可用的检测器。

【讨论】:

如何使用 tika-app1.8 检测 .properties 文件。它将它检测为文本/纯文本,而不是我希望它作为文本/属性。我该如何自定义? @kittu 您需要将其作为一个新问题提出和/或在 Tika 问题跟踪器中提出增强请求 解析器 jar 需要哪些依赖项?它们是在一个单独的罐子里吗? 关键是将 tika 解析包含在依赖项中(连同核心),然后您可以简单地使用 Tika.detect(tikaInputStream) 就可以完成这项工作。无需元数据、媒体类型或提取检测器。【参考方案2】:

对于遇到类似问题但使用较新 Tika 版本的其他人来说,这应该可以解决问题:

    使用ZipContainerDetector,因为您可能不再有ContainerAwareDetector。 为检测器的detect() 方法提供TikaInputStream,以确保tika 可以分析正确的mime 类型。

我的示例代码如下所示:

public static String getMimeType(final Document p_document)

    try
    
        Metadata metadata = new Metadata();
        metadata.add(Metadata.RESOURCE_NAME_KEY, p_document.getDocName());

        Detector detector = getDefaultDectector();

        LogMF.debug(log, "Trying to detect mime type with detector 0.", detector);
        TikaInputStream inputStream = TikaInputStream.get(p_document.getData(), metadata);

        return detector.detect(inputStream, metadata).toString();
    
    catch (Throwable t)
    
        log.error("Error while determining mime-type of " + p_document);
    

    return null;


private static Detector getDefaultDectector()

    if (detector == null)
    
        List<Detector> detectors = new ArrayList<>();

        // zip compressed container types
        detectors.add(new ZipContainerDetector());
        // Microsoft stuff
        detectors.add(new POIFSContainerDetector());
        // mime magic detection as fallback
        detectors.add(MimeTypes.getDefaultMimeTypes());

        detector = new CompositeDetector(detectors);
    

    return detector;

请注意,Document 类是我的域模型的一部分。所以你肯定会在那一行有类似的东西。

我希望有人可以使用它。

【讨论】:

您最好只使用 DefaultDetector,而不是尝试自己调用单个检测器 我无法使用默认检测器检测到 word 2010 文档的 mime 类型。使用我的方法我可以。但我还没有针对其他文档类型对其进行过测试。 DefaultDetector 应该可以解决这个问题(有大量单元测试表明这一点!)。确保你的类路径中有 Tika Parsers jar,以及依赖项(如果没有) 我希望没有人使用捕获Throwable并返回null的代码。【参考方案3】:

tika-core 中的默认字节模式检测规则只能检测所有 MS Office 文档类型使用的通用 OLE2 或 ZIP 格式。您想使用 ContainerAwareDetector 进行这种检测 afaik。并使用 MimeTypes 检测器作为其后备检测器。试试这个:

public MediaType getContentType(InputStream is, String fileName) 
    MediaType mediaType;
    Metadata md = new Metadata();
    md.set(Metadata.RESOURCE_NAME_KEY, fileName);
    Detector detector = new ContainerAwareDetector(tikaConfig.getMimeRepository());

    try 
        mediaType = detector.detect(is, md);
     catch (IOException ioe) 
        whatever;
    
    return mediaType;

这样你的测试应该可以通过

【讨论】:

ContainerAwareDetector 在 Tika 中已经被弃用了一段时间,对于今天看到这个的人来说,你应该使用 Tika's new-ish DefaultDetector 以及类路径中的所有 tika 解析器【参考方案4】:

您可以使用自定义 tika 配置文件:

MimeTypes mimes=MimeTypesFactory.create(Thread.currentThread()
   .getContextClassLoader().getResource("tika-custom-MimeTypes.xml"));
Metadata metadata = new Metadata();
metadata.add(Metadata.RESOURCE_NAME_KEY, file.getName());
tis = TikaInputStream.get(file);
String mimetype = new  DefaultDetector(mimes).detect(tis,metadata).toString();

在 WEB-INF/classes 中将“tika-custom-MimeTypes.xml”与您的更改放在一起:

就我而言:

<mime-type type="video/mp4">
    <magic priority="60">
      <match value="ftypmp41" type="string" offset="4"/>
      <match value="ftypmp42" type="string" offset="4"/>
      <!-- add -->
      <match value="ftyp" type="string" offset="4"/>
    </magic>
    <glob pattern="*.mp4"/>
    <glob pattern="*.mp4v"/>
    <glob pattern="*.mpg4"/>
    <!-- sub-class-of type="video/quicktime" /-->
</mime-type>
<mime-type type="video/quicktime">
    <magic priority="50">
      <match value="moov" type="string" offset="4"/>
      <match value="mdat" type="string" offset="4"/>
      <!--remove for videos of screencast -->
      <!--match value="ftyp" type="string" offset="4"/-->
    </magic>
    <glob pattern="*.qt"/>
    <glob pattern="*.mov"/>
</mime-type>

【讨论】:

以上是关于使用 Apache tika 获取 MimeType 子类型的主要内容,如果未能解决你的问题,请参考以下文章

Apache Tika 和文件访问而不是 Java 输入流

Python - Apache Tika 单页解析器

Apache Tika 服务器 - 请求标头参数?

apache tika 可以导出excel吗

跟踪更改的docx在Apache Tika中产生错误的输出

Apache Tika 提取扫描的 PDF 文件