Tika提取文檔調研測試

測試 Tika

Apache Tika用於文件類型檢測和從各種格式的文件中提取文本內容。

Tika的使用方式很簡單:

    public void testTikaParseEmlWithDocAttachment() throws TikaException, SAXException, IOException {
        String filename="eml-with-doc-attachment-test.eml";
        AutoDetectParser autoParser = new AutoDetectParser();
        ContentHandler contentHandler = new ToXMLContentHandler();
        Metadata metadata = new Metadata();
        
        InputStream stream =this.getClass().getClassLoader().getResourceAsStream(Paths.get(inputDir,filename).toString());
        autoParser.parse(stream, contentHandler, metadata);
    }

其中,autoParser可以自動輸入的檢測文檔的類型,調用合適的解析器去解析文檔,將結果寫入到Metadata和ContentHandler中,Metadata保存的是輸入文檔的元信息,
比如文檔的創建人創建時間等等。文檔的內容的內容保存在ContentHandler的Writer中。如果想要將文檔輸出爲xhtml格式,可以選用ToXMLContentHandler類型的參數,
如果只想要輸出文檔的內容,可以用BodyContentHandler類型的參數。

Eml

對於eml文件,直接用ToXMLContentHandler或者BodyContentHandler可以將eml文件提取爲xhtml或者純文本的格式。但是有一個問題就是郵件的正文和附件都會拼接到一起,
合成爲一個文件,如果是想要將每個文件的內容單獨提取出來,就需要換一種方式來提取了:

    public List<Metadata> extract(InputStream inputStream) throws TikaException, SAXException, IOException {
        Parser p = new AutoDetectParser();
        ContentHandlerFactory factory = new BasicContentHandlerFactory(
                BasicContentHandlerFactory.HANDLER_TYPE.HTML, -1);

        RecursiveParserWrapper wrapper = new RecursiveParserWrapper(p);
        Metadata metadata = new Metadata();
        //metadata.set(RESOURCE_NAME_KEY, "test_recursive_embedded.docx");
        ParseContext context = new ParseContext();
        RecursiveParserWrapperHandler handler = new RecursiveParserWrapperHandler(factory, -1);

        wrapper.parse(inputStream, handler, metadata, context);
        return handler.getMetadataList();
    }

這種方式,提取的結果會被存入到一個Metadata中的List中,list中的每個元素都是HashMap,一個HashMap對應郵件的正文或者郵件的附件或者是附件
中嵌入的文件,比如word中的圖片,或者是壓縮包中的文件。而HashMap的內容則包含對應文件的內容和元信息。
其中X-TIKA:embedded_depth的值表示的是對應文件嵌套深度,X-TIKA:embedded_resource_path表示的是從當前文件是嵌在哪個文件中的。

而如果想提取eml文件的附件:

public class EmbeddedFilesExtractor {
    private Parser parser = new AutoDetectParser();
    private Detector detector = ((AutoDetectParser) parser).getDetector();
    private TikaConfig config = TikaConfig.getDefaultConfig();
    String RESOURCE_NAME_KEY = "resourceName";
    public void extract(InputStream is, Path outputDir) throws SAXException, TikaException, IOException {
        Metadata m = new Metadata();
        ParseContext c = new ParseContext();
        ContentHandler h = new BodyContentHandler(-1);

        c.set(Parser.class, parser);
        EmbeddedDocumentExtractor ex = new MyEmbeddedDocumentExtractor(outputDir, c);
        c.set(EmbeddedDocumentExtractor.class, ex);

        parser.parse(is, h, m, c);
    }

    private class MyEmbeddedDocumentExtractor extends ParsingEmbeddedDocumentExtractor {
        private final Path outputDir;
        private int fileCount = 0;

        private MyEmbeddedDocumentExtractor(Path outputDir, ParseContext context) {
            super(context);
            this.outputDir = outputDir;
        }

        @Override
        public boolean shouldParseEmbedded(Metadata metadata) {
            return true;
        }

        @Override
        public void parseEmbedded(InputStream stream, ContentHandler handler, Metadata metadata, boolean outputHtml)
                throws SAXException, IOException {

            //try to get the name of the embedded file from the metadata
            String name = metadata.get(RESOURCE_NAME_KEY);

            if (name == null) {
                name = "file_" + fileCount++;
            } else {
                //make sure to select only the file name (not any directory paths
                //that might be included in the name) and make sure
                //to normalize the name
                name = name.replaceAll("\u0000", " ");
                int prefix = FilenameUtils.getPrefixLength(name);
                if (prefix > -1) {
                    name = name.substring(prefix);
                }
                name = FilenameUtils.normalize(FilenameUtils.getName(name));
            }

            //now try to figure out the right extension for the embedded file
            MediaType contentType = detector.detect(stream, metadata);

            if (name.indexOf('.') == -1 && contentType != null) {
                try {
                    name += config.getMimeRepository().forName(
                            contentType.toString()).getExtension();
                } catch (MimeTypeException e) {
                    e.printStackTrace();
                }
            }

            Path outputFile = outputDir.resolve(name);
            if (Files.exists(outputFile)) {
                outputFile = outputDir.resolve(UUID.randomUUID().toString()+"-"+name);
            }
            Files.createDirectories(outputFile.getParent());
            Files.copy(stream, outputFile);
        }
    }
}

這種方式也可以提取壓縮包內的文件或者是word中的圖片。但是不會再遞歸的提取下去,只提取當前輸入文件所直接包含的文件。

可提取的元信息

郵件主題
創建人
創建時間
發件人
收件人
MIME-version
無附件的元信息

輸出形式

可以輸出爲xhtml文件,純文本文件,可提取郵件附件文件,可分別提取郵件的正文和附件

Word

可提取的元信息

標題
作者
最後一次保存者
創建時間
最後一次修改時間
最後一次打印時間
文檔字數
文檔類型
頁數
模板

輸出形式

可以輸出爲xhtml文件,和純文本文件,可以保留word中的表格結構。
可以提取頁眉頁腳批註,但是失去了位置信息
word中的圖片,在輸出爲xhtml文件的時候,命名規則是從imagen加圖像擴展名的格式

Excel

###可提取的元信息
標題
作者
最後一次保存者
創建時間
最後一次修改時間
最後一次打印時間
文檔類型

輸出形式

可以輸出爲xhtml文件,和純文本文件,可以保留Excel中的表格結構。

PPT

可提取的元信息

標題
作者
最後一次保存者
創建時間
最後一次修改時間
最後一次打印時間
文檔字數
文檔類型
頁數
模板

輸出形式

可以輸出爲xhtml文件,可以保留PPT中的表格結構。

PDF

可提取的元信息

標題
作者
創建時間
最後一次修改時間
文檔類型
頁數
創建工具

輸出形式

可以輸出爲xhtml文件,可以保留word中的表格結構。

壓縮包

支持的壓縮包格式:
Tar, AR, ARJ, CPIO, Dump, Zip, 7Zip, Gzip, BZip2, XZ, LZMA, Z and Pack200.

遺留問題

提取eml-with-doc-attachment-test.eml中的word附件文件名亂碼導致是輸出失敗問題。
提取eml-with-table.eml文件時文件內容亂碼問題

國內的git倉庫地址 方便下載

[email protected]:null_412_9848/TikaTest.git

持續更新中 歡迎評論交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章