什么是 Apache Tika?
Apache Tika 是一个内容分析工具包,用于从各种格式的文档中检测和提取元数据及文本内容。Tika 支持检测和解析超过 1000 种文件类型,包括:
- 文档格式:PDF、Microsoft Office、OpenDocument、RTF、HTML 等
- 图片格式:JPEG、PNG、TIFF 等(可提取 EXIF 元数据)
- 邮件格式:MIME 消息
- 归档格式:ZIP、TAR、GZIP 等
- 音频/视频:MP3、MP4、AVI 等的元数据
- 其他:XML、JSON、CSV 等结构化格式
项目集成
Maven 依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<properties>
<tika.version>3.2.2</tika.version>
</properties>
<dependencies>
<!-- 核心库 -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika.version}</version>
</dependency>
<!-- 解析器包(包含所有主流文档格式的解析器) -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>${tika.version}</version>
</dependency>
</dependencies>
|
注意:tika-parsers-standard-package 会包含所有解析器,如果需要更小的包体积,可以只引入特定解析器。
基础使用
1. 简单文本提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import org.apache.tika.Tika;
import org.springframework.web.multipart.MultipartFile;
@Service
public class DocumentService {
private final Tika tika = new Tika();
public String extractText(MultipartFile file) throws Exception {
try (InputStream inputStream = file.getInputStream()) {
return tika.parseToString(inputStream);
}
}
}
|
2. 自动检测文件类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import org.apache.tika.Tika;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
@Service
public class ContentTypeDetectionService {
private final Tika tika = new Tika();
public String detectContentType(MultipartFile file) {
try (InputStream inputStream = file.getInputStream()) {
// 根据文件内容检测类型
return tika.detect(inputStream, file.getOriginalFilename());
} catch (Exception e) {
// 检测失败时回退到 HTTP Content-Type
return file.getContentType();
}
}
}
|
进阶使用
使用 Parser API 进行精细控制
在项目实践中,我们使用 AutoDetectParser 和 ParseContext 进行更精细的控制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.BodyContentHandler;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.exception.TikaException;
import org.apache.tika.extractor.EmbeddedDocumentExtractor;
import org.apache.tika.parser.pdf.PDFParserConfig;
import org.springframework.web.multipart.MultipartFile;
@Service
public class DocumentParseService {
private static final int MAX_TEXT_LENGTH = 5 * 1024 * 1024; // 5MB
public String parseContent(MultipartFile file) throws Exception {
try (InputStream inputStream = file.getInputStream()) {
return parseContent(inputStream);
}
}
private String parseContent(InputStream inputStream)
throws TikaException, IOException, SAXException {
// 1. 创建自动检测解析器
AutoDetectParser parser = new AutoDetectParser();
// 2. 创建内容处理器,限制最大文本长度
BodyContentHandler handler = new BodyContentHandler(MAX_TEXT_LENGTH);
// 3. 创建元数据对象
Metadata metadata = new Metadata();
// 4. 创建解析上下文
ParseContext context = new ParseContext();
// 5. 显式指定 Parser 到 Context(增强健壮性)
context.set(Parser.class, parser);
// 6. 禁用嵌入文档解析(避免提取图片引用和临时文件路径)
context.set(EmbeddedDocumentExtractor.class,
new NoOpEmbeddedDocumentExtractor());
// 7. PDF 专用配置:关闭图片提取,按位置排序文本
PDFParserConfig pdfConfig = new PDFParserConfig();
pdfConfig.setExtractInlineImages(false);
pdfConfig.setSortByPosition(true);
context.set(PDFParserConfig.class, pdfConfig);
// 8. 执行解析
parser.parse(inputStream, handler, metadata, context);
return handler.toString();
}
}
|
禁用嵌入资源解析
在解析 PDF 等文档时,Tika 默认会尝试提取嵌入的图片和附件,这可能导致提取到无用的临时文件路径:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import org.apache.tika.extractor.EmbeddedDocumentExtractor;
// 自定义空提取器,不处理嵌入资源
public class NoOpEmbeddedDocumentExtractor implements EmbeddedDocumentExtractor {
@Override
public boolean shouldParseEmbedded(Metadata metadata) {
return false; // 不解析任何嵌入资源
}
@Override
public void parseEmbedded(InputStream inputStream, ContentHandler contentHandler,
Metadata metadata, boolean outputHtml) throws IOException {
// 空实现,不做任何提取
}
}
|
提取元数据
获取文档元数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
public void extractMetadata(InputStream inputStream) throws Exception {
AutoDetectParser parser = new AutoDetectParser();
BodyContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
parser.parse(inputStream, handler, metadata);
// 获取常用元数据
System.out.println("标题: " + metadata.get(TikaCoreProperties.TITLE));
System.out.println("作者: " + metadata.get(TikaCoreProperties.CREATOR));
System.out.println("创建日期: " + metadata.get(TikaCoreProperties.CREATED));
System.out.println("修改日期: " + metadata.get(TikaCoreProperties.MODIFIED));
System.out.println("文件类型: " + metadata.get("Content-Type"));
// 打印所有元数据
for (String name : metadata.names()) {
System.out.println(name + ": " + metadata.get(name));
}
}
|
PDF 专用元数据
1
2
3
4
5
6
7
|
import org.apache.tika.metadata.PDF;
// PDF 特有元数据
System.out.println("PDF 标题: " + metadata.get(PDF.TITLE));
System.out.println("PDF 作者: " + metadata.get(PDF.AUTHOR));
System.out.println("PDF 创建者: " + metadata.get(PDF.CREATOR));
System.out.println("页数: " + metadata.getInt(PDF.NUMBER_OF_PAGES));
|
解析特定格式
解析 PDF
1
2
3
4
5
6
7
8
9
10
11
|
import org.apache.tika.parser.pdf.PDFParser;
import org.apache.tika.parser.pdf.PDFParserConfig;
PDFParser parser = new PDFParser();
PDFParserConfig config = new PDFParserConfig();
config.setExtractInlineImages(false); // 不提取内联图片
config.setSortByPosition(true); // 按位置排序文本(适合中文)
config.setSuppressDuplicateOverlappingText(true); // 抑制重复文本
ParseContext context = new ParseContext();
context.set(PDFParserConfig.class, config);
|
解析 Microsoft Office 文档
1
2
3
4
5
6
7
8
9
10
11
|
import org.apache.tika.parser.microsoft.ooxml.OOXMLParser;
import org.apache.tika.parser.microsoft.ParserFactory;
import org.apache.tika.parser.microsoft.OfficeParserConfig;
// Word、Excel、PowerPoint 都使用 OOXMLParser
OOXMLParser parser = new OOXMLParser();
OfficeParserConfig config = new OfficeParserConfig();
config.setConcatenatePhrases(true); // 合并短语
ParseContext context = new ParseContext();
context.set(OfficeParserConfig.class, config);
|
解析 HTML
1
2
3
4
5
6
7
8
9
10
11
|
import org.apache.tika.parser.html.HtmlParser;
import org.apache.tika.parser.html.HtmlParserConfig;
// 包含 Jsoup 解析器配置
HtmlParser parser = new HtmlParser();
HtmlParserConfig config = new HtmlParserConfig();
config.setJavascript(false); // 不执行 JavaScript
config.setMaxTextLength(10000); // 最大文本长度
ParseContext context = new ParseContext();
context.set(HtmlParserConfig.class, config);
|
文本清洗处理
Tika 提取的文本通常需要进一步清洗才能用于后续处理(如 RAG、知识库等):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
@Service
public class TextCleaningService {
// 图片文件名行:image123.png
private static final Pattern IMAGE_FILENAME_LINE =
Pattern.compile("(?m)^image\\d+\\.(png|jpe?g|gif|bmp|webp)\\s*$");
// HTTP/HTTPS 图片链接
private static final Pattern IMAGE_URL =
Pattern.compile("https?://\\S+?\\.(png|jpe?g|gif|bmp|webp)(\\?\\S*)?",
Pattern.CASE_INSENSITIVE);
// 文件协议 URL(Tika PDF 临时文件路径等)
private static final Pattern FILE_URL =
Pattern.compile("file:(//)?\\S+", Pattern.CASE_INSENSITIVE);
// 分隔线
private static final Pattern SEPARATOR_LINE =
Pattern.compile("(?m)^\\s*[-_*=]{3,}\\s*$");
// 控制字符
private static final Pattern CONTROL_CHARS =
Pattern.compile("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F]");
public String cleanText(String text) {
if (text == null || text.isBlank()) {
return "";
}
String t = text;
// 语义去噪
t = CONTROL_CHARS.matcher(t).replaceAll("");
t = IMAGE_FILENAME_LINE.matcher(t).replaceAll("");
t = IMAGE_URL.matcher(t).replaceAll("");
t = FILE_URL.matcher(t).replaceAll("");
t = SEPARATOR_LINE.matcher(t).replaceAll("");
// 格式规范化
t = t.replace("\r\n", "\n").replace("\r", "\n");
t = t.replaceAll("(?m)[ \t]+$", "");
t = t.replaceAll("\\n{3,}", "\n\n");
return t.strip();
}
}
|
最佳实践
1. 使用流式处理大文件
1
2
3
4
5
|
// 使用 TikaInputStream 处理大文件
import org.apache.tika.io.TikaInputStream;
TikaInputStream tis = TikaInputStream.of(file.toPath());
parser.parse(tis, handler, metadata, context);
|
2. 设置合理的超时
1
2
3
4
|
import org.apache.tika.parser.ParseContext;
ParseContext context = new ParseContext();
context.set("parseMax.timeout", "300000"); // 5 分钟超时
|
3. 处理编码问题
1
2
3
4
5
6
|
// 对于纯文本文件,指定编码
Metadata metadata = new Metadata();
metadata.set(Metadata.CONTENT_TYPE, "text/plain; charset=UTF-8");
InputStreamReader reader = new InputStreamReader(
file.getInputStream(), StandardCharsets.UTF_8);
|
4. 并行解析多个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Async
public CompletableFuture<List<String>> parseDocuments(List<MultipartFile> files) {
return CompletableFuture.supplyAsync(() ->
files.parallelStream()
.map(file -> {
try {
return parseContent(file);
} catch (Exception e) {
return "";
}
})
.collect(Collectors.toList())
);
}
|
支持的文件类型一览
| 类别 |
格式 |
| 文档 |
PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, ODF, RTF, TXT |
| 网页 |
HTML, XHTML, XML |
| 邮件 |
MIME, EML, PST, OST |
| 图片 |
JPEG, PNG, GIF, TIFF, BMP, WebP |
| 归档 |
ZIP, TAR, GZIP, BZIP2, 7Z |
| 音频 |
MP3, WAV, FLAC, OGG |
| 视频 |
MP4, AVI, MOV, WMV, FLV |
| 设计 |
PSD, AI, Sketch |
性能优化建议
- 使用
AutoDetectParser 自动检测:智能选择解析器
- 禁用不需要的解析器:减少启动时间和内存占用
- 设置
MAX_TEXT_LENGTH:防止无限提取
- 使用流式处理:避免一次性加载整个文件到内存
- 禁用嵌入资源提取:如不需要图片和附件
总结
Apache Tika 是 Java 项目中文档解析的首选工具,它提供了:
- 统一的 API 来处理多种文档格式
- 自动检测文件类型
- 丰富的元数据提取能力
- 可配置的解析行为
- 良好的扩展性
通过本指南,你应该能够使用 Tika 构建文档解析服务,实现简历解析、知识库构建、文档检索等功能。