Contents

Apache Tika 简介

1. 概述

Apache Tika 是一个工具包,用于从各种类型的文档中提取内容和元数据,例如 Word、Excel 和 PDF,甚至是 JPEG 和 MP4 等多媒体文件。

所有基于文本的和多媒体文件都可以使用一个通用界面进行解析,使 Tika 成为一个功能强大且用途广泛的内容分析库。

在本文中,我们将介绍 Apache Tika,包括它的解析 API 以及它如何自动检测文档的内容类型。还将提供工作示例来说明该库的操作。

2. 入门

为了使用 Apache Tika 解析文档,我们只需要一个 Maven 依赖项:

<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers</artifactId>
    <version>1.17</version>
</dependency>

可以在此处 找到此工件的最新版本。

3. Parser API

Parser API 是 Apache Tika 的核心,抽象出解析操作的复杂性。此 API 依赖于一个方法:

void parse(
  InputStream stream, 
  ContentHandler handler, 
  Metadata metadata, 
  ParseContext context) 
  throws IOException, SAXException, TikaException

该方法的参数含义如下:

  • stream – 从要解析的文档创建的InputStream实例
  • handler – 一个ContentHandler对象,接收从输入文档解析的一系列 XHTML SAX 事件;然后,此处理程序将处理事件并以特定形式导出结果
  • Metadata — 一个Metadata对象,在解析器内外传递元数据属性
  • context – 一个ParseContext实例,携带特定于上下文的信息,用于自定义解析过程

如果无法从输入流中读取,则 parse 方法抛出 IOException,如果无法解析从stream中获取的文档,则抛出* TikaException*,如果处理程序无法处理事件,则抛出SAXException

在解析文档时,Tika 会尽可能地重用现有的解析器库,例如 Apache POI 或 PDFBox。因此,大多数Parser实现类只是这些外部库的适配器。

在第 5 节中,我们将了解如何使用handlerMetadata参数来提取文档的内容和元数据。

为方便起见,我们可以使用外观类Tika来访问Parser API 的功能。

4. 自动检测

Apache Tika 可以根据文档本身而不是附加信息自动检测文档的类型及其语言。

4.1.文档类型检测

文档类型的检测可以使用Detector接口的实现类来完成,它有一个方法:

MediaType detect(java.io.InputStream input, Metadata metadata) 
  throws IOException

此方法获取一个文档及其关联的元数据,然后返回一个MediaType对象,该对象描述有关文档类型的最佳猜测。 元数据并不是检测器所依赖的唯一信息来源。检测器还可以使用魔术字节,这是文件开头附近的一种特殊模式,或者将检测过程委托给更合适的检测器。

事实上,检测器使用的算法是依赖于实现的。

例如,默认检测器首先使用魔术字节,然后是元数据属性。如果此时还没有找到内容类型,它将使用服务加载器来发现所有可用的检测器并依次尝试它们。

4.2. 语言检测

除了文档的类型,即使没有元数据信息的帮助,Tika 也可以识别其语言。

在以前的 Tika 版本中,使用LanguageIdentifier实例检测文档的语言。

但是,LanguageIdentifier已被弃用,取而代之的是 Web 服务,这在Getting Started 文档中没有明确说明。

现在通过抽象类LanguageDetector的子类型提供语言检测服务。使用网络服务,您还可以访问成熟的在线翻译服务,例如谷歌翻译或微软翻译。

为简洁起见,我们不会详细介绍这些服务。

5. Tika 示例

本节使用工作示例说明 Apache Tika 功能。

插图方法将包装在一个类中:

public class TikaAnalysis {
    // illustration methods
}

5.1.检测文档类型

下面是我们可以用来检测从InputStream读取的文档类型的代码:

public static String detectDocTypeUsingDetector(InputStream stream) 
  throws IOException {
    Detector detector = new DefaultDetector();
    Metadata metadata = new Metadata();
    MediaType mediaType = detector.detect(stream, metadata);
    return mediaType.toString();
}

假设我们在类路径中有一个名为tika.txt的 PDF 文件。该文件的扩展名已更改以试图欺骗我们的分析工具。通过测试仍然可以找到并确认文档的真实类型:

@Test
public void whenUsingDetector_thenDocumentTypeIsReturned() 
  throws IOException {
    InputStream stream = this.getClass().getClassLoader()
      .getResourceAsStream("tika.txt");
    String mediaType = TikaAnalysis.detectDocTypeUsingDetector(stream);
    assertEquals("application/pdf", mediaType);
    stream.close();
}

很明显,错误的文件扩展名无法阻止 Tika 找到正确的媒体类型,这要归功于文件开头的魔术字节*%PDF *。

为方便起见,我们可以使用Tika门面类重新编写检测代码,结果相同:

public static String detectDocTypeUsingFacade(InputStream stream) 
  throws IOException {
 
    Tika tika = new Tika();
    String mediaType = tika.detect(stream);
    return mediaType;
}

5.2. 提取内容

现在让我们提取文件的内容并将结果作为String返回——使用Parser API:

public static String extractContentUsingParser(InputStream stream) 
  throws IOException, TikaException, SAXException {
 
    Parser parser = new AutoDetectParser();
    ContentHandler handler = new BodyContentHandler();
    Metadata metadata = new Metadata();
    ParseContext context = new ParseContext();
    parser.parse(stream, handler, metadata, context);
    return handler.toString();
}

给定类路径中包含以下内容的 Microsoft Word 文件:

Apache Tika - a content analysis toolkit
The Apache Tika™ toolkit detects and extracts metadata and text ...

内容可以提取和验证:

@Test
public void whenUsingParser_thenContentIsReturned() 
  throws IOException, TikaException, SAXException {
    InputStream stream = this.getClass().getClassLoader()
      .getResourceAsStream("tika.docx");
    String content = TikaAnalysis.extractContentUsingParser(stream);
    assertThat(content, 
      containsString("Apache Tika - a content analysis toolkit"));
    assertThat(content, 
      containsString("detects and extracts metadata and text"));
    stream.close();
}

同样,使用Tika类可以更方便地编写代码:

public static String extractContentUsingFacade(InputStream stream) 
  throws IOException, TikaException {
 
    Tika tika = new Tika();
    String content = tika.parseToString(stream);
    return content;
}

5.3. 提取元数据

除了文档的内容,Parser API 还可以提取元数据:

public static Metadata extractMetadatatUsingParser(InputStream stream) 
  throws IOException, SAXException, TikaException {
 
    Parser parser = new AutoDetectParser();
    ContentHandler handler = new BodyContentHandler();
    Metadata metadata = new Metadata();
    ParseContext context = new ParseContext();
    parser.parse(stream, handler, metadata, context);
    return metadata;
}

当类路径中存在 Microsoft Excel 文件时,此测试用例确认提取的元数据是正确的:

@Test
public void whenUsingParser_thenMetadataIsReturned() 
  throws IOException, TikaException, SAXException {
    InputStream stream = this.getClass().getClassLoader()
      .getResourceAsStream("tika.xlsx");
    Metadata metadata = TikaAnalysis.extractMetadatatUsingParser(stream);
    assertEquals("org.apache.tika.parser.DefaultParser", 
      metadata.get("X-Parsed-By"));
    assertEquals("Microsoft Office User", metadata.get("Author"));
    stream.close();
}

最后,这是使用Tika门面类的另一个版本的提取方法:

public static Metadata extractMetadatatUsingFacade(InputStream stream) 
  throws IOException, TikaException {
    Tika tika = new Tika();
    Metadata metadata = new Metadata();
    tika.parse(stream, metadata);
    return metadata;
}