Apache Tika
面向 Java/Spring 项目的文档解析与文本抽取工具入门与实践(以 Tika 2.x 为主)。
1. Tika 是什么
Apache Tika 是一个用于检测文件类型(MIME/媒体类型) 、抽取文本与元数据的内容分析工具库。
典型用途:
- 文档上传后,自动识别格式(PDF/Word/Excel/PPT/HTML/图片等)
- 从文档中抽取纯文本,用于搜索索引、向量化(Embedding)、RAG 知识库
- 抽取元数据(作者、标题、创建时间、页数、图片尺寸、编码等)
- 统一处理不同格式文档的解析差异
2. 组件与依赖(Tika 2.x)
2.1 常见 Maven 依赖
你项目里已引入(示例):
xml
<!-- Apache Tika for document parsing -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>2.9.1</version>
</dependency>
说明:
tika-core:核心 API(Tika、Detector、Metadata等)tika-parsers-standard-package:常用解析器集合(PDF、Office、HTML、音视频等)
建议:解析能力不足时再引入更大包或额外依赖;保持依赖可控,避免引入过多解析器带来体积与安全面扩大。
3. 核心概念与工作流程
3.1 MIME 类型检测(Detector)
Tika 可以通过文件头(magic bytes)+ 文件名等信息推断 MIME:
application/pdfapplication/vnd.openxmlformats-officedocument.wordprocessingml.documenttext/plain
3.2 解析与抽取(Parser)
解析的本质:
- 输入:
InputStream+Metadata+ParseContext - 输出:
- 文本:通过
ContentHandler(常用BodyContentHandler) - 元数据:写入
Metadata
- 文本:通过
3.3 一站式门面(Tika)
org.apache.tika.Tika 提供便捷方法:
detect(InputStream):识别类型parseToString(InputStream):抽取文本
适合快速上手,但可控性不如直接使用 Parser。
4. 快速上手示例
4.1 detect:识别文件类型
java
import org.apache.tika.Tika;
try (InputStream is = file.getInputStream()) {
Tika tika = new Tika();
String mimeType = tika.detect(is, file.getOriginalFilename());
System.out.println("mimeType = " + mimeType);
}
4.2 parseToString:抽取纯文本(快速版)
java
import org.apache.tika.Tika;
try (InputStream is = file.getInputStream()) {
Tika tika = new Tika();
String text = tika.parseToString(is);
// 注意:text 可能很长
}
4.3 使用 Parser(可控版,推荐用于生产)
java
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;
import java.io.InputStream;
public class TikaParseResult {
private final String text;
private final Metadata metadata;
public TikaParseResult(String text, Metadata metadata) {
this.text = text;
this.metadata = metadata;
}
public String getText() { return text; }
public Metadata getMetadata() { return metadata; }
}
public TikaParseResult parse(InputStream is) throws Exception {
AutoDetectParser parser = new AutoDetectParser();
Metadata metadata = new Metadata();
ParseContext context = new ParseContext();
// 关键点:BodyContentHandler 默认有字符数限制,建议显式放开或设置合理上限
BodyContentHandler handler = new BodyContentHandler(-1);
parser.parse(is, handler, metadata, context);
String text = handler.toString();
return new TikaParseResult(text, metadata);
}
AutoDetectParser会自动选择合适的解析器(PDFParser、OOXMLParser 等)。
5. 元数据(Metadata)怎么用
5.1 常见元数据键
不同格式会有不同键,但常见包括:
Metadata.CONTENT_TYPE:内容类型(MIME)TikaCoreProperties.TITLE:标题TikaCoreProperties.CREATOR:作者TikaCoreProperties.CREATED:创建时间XMPTPg.N_PAGES:页数(如 PDF)
示例:
java
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
String contentType = metadata.get(Metadata.CONTENT_TYPE);
String title = metadata.get(TikaCoreProperties.TITLE);
String creator = metadata.get(TikaCoreProperties.CREATOR);
5.2 以 Map 形式输出元数据
java
Map<String, String> metaMap = new HashMap<>();
for (String name : metadata.names()) {
metaMap.put(name, metadata.get(name));
}
6. 生产实践与常见坑
6.1 BodyContentHandler 的长度限制
BodyContentHandler 默认会限制输出字符数(防止 OOM)。
new BodyContentHandler(-1):不限制(可能有 OOM 风险)- 推荐:设定一个合理上限,比如 5MB/10MB 文本量,超出后给出提示或分批处理。
6.2 内存与大文件
Tika 解析大 PDF/Office 可能占用较多内存。
建议:
- 限制上传大小
- 使用流式处理(尽量不把整个文件读入 byte[])
- 对超大文件做异步解析(消息队列/任务队列)
6.3 安全:Zip Bomb / 解析器漏洞
Office OpenXML、本质是 zip 容器,需防范 zip bomb。
建议:
- 永远使用最新修复版本(Tika/POI/PDFBox)
- 做文件大小、解压比限制(必要时)
- 在隔离环境(容器)运行解析服务(高安全要求场景)
6.4 字符清洗与换行
抽取文本常见问题:
- 过多空行、页眉页脚重复
- 连字符断行
- 控制字符
可做基础清洗:
java
public static String normalizeText(String s) {
if (s == null) return "";
// 替换 \r\n 为 \n,压缩连续空白行
s = s.replace("\r\n", "\n");
s = s.replaceAll("[\\u0000-\\u001F&&[^\\n\\t]]", " ");
s = s.replaceAll("\\n{3,}", "\n\n");
return s.trim();
}
6.5 解析超时
某些复杂 PDF/损坏文件可能解析很慢。
建议:
- 在业务层面加超时控制(Future/线程池/请求超时)
- 失败降级:记录失败原因、允许重试、跳过该文件
7. 单元测试建议
对每种核心格式准备小样本:
- pdf/docx/xlsx/pptx/txt/html
测试点:
- 能否 detect 到正确 MIME
- 抽取文本是否非空
- 元数据是否有关键字段
示例(JUnit):
java
@Test
void parse_pdf_should_extract_text() throws Exception {
try (InputStream is = getClass().getResourceAsStream("/samples/a.pdf")) {
TikaParseResult r = tikaParseService.parse(is, "a.pdf");
assertNotNull(r.getText());
assertTrue(r.getText().length() > 20);
}
}
8. 扩展阅读(建议关键词)
- Apache Tika 2.x 官方文档与 Javadoc
AutoDetectParser、Detector、Metadata、ParseContext- PDFBox/POI(Tika 底层常用依赖)
- Tesseract OCR(图片文字识别,Tika 也可集成,但需要额外组件)
9. 一个可复用的 Tika 解析服务(推荐封装)
java
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;
import org.springframework.stereotype.Service;
import java.io.InputStream;
@Service
public class TikaParseService {
public TikaParseResult parse(InputStream is, String originalFilename) {
try {
AutoDetectParser parser = new AutoDetectParser();
Metadata metadata = new Metadata();
if (originalFilename != null) {
metadata.set(Metadata.RESOURCE_NAME_KEY, originalFilename);
}
ParseContext context = new ParseContext();
BodyContentHandler handler = new BodyContentHandler(10 * 1024 * 1024); // 10MB 上限示例
parser.parse(is, handler, metadata, context);
return new TikaParseResult(handler.toString(), metadata);
} catch (Exception e) {
throw new RuntimeException("Tika 解析失败: " + e.getMessage(), e);
}
}
}