偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Spring Boot 整合 Apache Tika 實(shí)現(xiàn)文件類型檢測(cè)與內(nèi)容提取

開發(fā) 前端
在實(shí)際開發(fā)中,我們經(jīng)常需要處理各類文件(如PDF、Word、Excel、圖片等),核心需求包括識(shí)別文件真實(shí)類型(避免后綴名欺騙)和提取文件內(nèi)容/元數(shù)據(jù)(如文檔正文、創(chuàng)建時(shí)間、作者)。Apache Tika作為Apache基金會(huì)的開源項(xiàng)目,能高效解決這些問題,且無(wú)需手動(dòng)編寫不同格式的解析邏輯。

前言

在實(shí)際開發(fā)中,我們經(jīng)常需要處理各類文件(如PDF、Word、Excel、圖片等),核心需求包括識(shí)別文件真實(shí)類型(避免后綴名欺騙)和提取文件內(nèi)容/元數(shù)據(jù)(如文檔正文、創(chuàng)建時(shí)間、作者)。Apache Tika作為Apache基金會(huì)的開源項(xiàng)目,能高效解決這些問題,且無(wú)需手動(dòng)編寫不同格式的解析邏輯。

核心概念

在整合前,先明確Tika3個(gè)核心組件,理解其工作原理:

  • Detector(檢測(cè)器):負(fù)責(zé)識(shí)別文件的真實(shí)類型,支持通過文件頭、字節(jié)流、擴(kuò)展名等多維度檢測(cè),避免 “后綴名篡改” 導(dǎo)致的類型誤判。
  • Parser(解析器):根據(jù)Detector識(shí)別的文件類型,調(diào)用對(duì)應(yīng)的解析器提取文件內(nèi)容(如文本)和元數(shù)據(jù)(如文件大小、修改時(shí)間),Tika內(nèi)置了PDF、Office、XML等格式的解析器。
  • Metadata(元數(shù)據(jù)):存儲(chǔ)文件的結(jié)構(gòu)化信息,分為內(nèi)置元數(shù)據(jù)(如Metadata.CONTENT_ENCODING、Metadata.CONTENT_TYPE)和自定義元數(shù)據(jù)(如文檔作者、版本號(hào))。

案例

依賴添加

Tika依賴較多解析庫(kù)(如POI、PDFBox),可能與項(xiàng)目中已有的依賴沖突(如POI版本不一致),手動(dòng)排除即可。

<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-core</artifactId>
    <version>2.9.2</version>
</dependency>

<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers-standard-package</artifactId>
    <version>2.9.2</version>
</dependency>

配置 Tika Bean

為避免重復(fù)創(chuàng)建Tika實(shí)例(提升性能),支持自定義配置(如超時(shí)時(shí)間、解析器優(yōu)先級(jí)):

@Configuration
public class TikaSelfConfig {

    /**
     * 全局Tika實(shí)例(用于文件類型檢測(cè))
     */
    @Bean
    public Tika tika() throws TikaException, IOException, SAXException {
        // 自定義Tika配置:設(shè)置文件類型檢測(cè)超時(shí)時(shí)間(5秒)
        TikaConfig config = new TikaConfig();
        return new org.apache.tika.Tika(config) {
            @Override
            public String detect(java.io.InputStream stream, Metadata metadata) {
                try {
                    // 超時(shí)控制:避免解析超大文件阻塞
                    return super.detect(new TimeoutInputStream(stream, 5000), metadata);
                } catch (IOException e) {
                    throw new RuntimeException("文件類型檢測(cè)超時(shí)(超過5秒)", e);
                }
            }
        };
    }

    /**
     * 自動(dòng)檢測(cè)解析器(用于內(nèi)容提?。?     */
    @Bean
    public Parser autoDetectParser() {
        // AutoDetectParser會(huì)根據(jù)文件類型自動(dòng)選擇解析器
        return new AutoDetectParser();
    }

    // 注冊(cè)自定義解析器(在TikaConfig中添加)
    @Bean
    public Parser customParser() {
        return new CustomCsvParser();
    }
}


public class TimeoutInputStream extends InputStream {
    private final InputStream delegate;
    private final long timeoutMillis;
    private long lastReadTime;

    public TimeoutInputStream(InputStream delegate, long timeoutMillis) {
        this.delegate = delegate;
        this.timeoutMillis = timeoutMillis;
        this.lastReadTime = System.currentTimeMillis();
    }

    @Override
    public int read() throws IOException {
        checkTimeout();
        int data = delegate.read();
        if (data != -1) {
            lastReadTime = System.currentTimeMillis();
        }
        return data;
    }

    private void checkTimeout() throws IOException {
        long elapsed = System.currentTimeMillis() - lastReadTime;
        if (elapsed > timeoutMillis) {
            throw new IOException("Stream read timeout (elapsed: " + elapsed + "ms)");
        }
    }

    // 重寫其他read方法(read(byte[]), read(byte[], int, int)),邏輯類似
}

功能 1:文件類型檢測(cè)

@Service
public class TikaFileDetectService {

    private final Tika tika;

    // 注入全局Tika Bean
    public TikaFileDetectService(Tika tika) {
        this.tika = tika;
    }

    /**
     * 1. 基于MultipartFile(文件流)檢測(cè)真實(shí)類型(推薦)
     * @param file 上傳的文件
     * @return 真實(shí)MIME類型(如image/jpeg、application/pdf)
     */
    public String detectFileByStream(MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("文件不能為空");
        }

        // 元數(shù)據(jù):可添加文件名輔助檢測(cè)(非必需,但能提升準(zhǔn)確率)
        Metadata metadata = new Metadata();
        metadata.add(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());

        // 通過文件流檢測(cè)(Tika會(huì)讀取文件頭字節(jié),不依賴后綴名)
        try (InputStream inputStream = file.getInputStream()) {
            return tika.detect(inputStream, metadata);
        }
    }

    /**
     * 2. 基于字節(jié)數(shù)組檢測(cè)(適用于小文件/內(nèi)存中的文件)
     * @param bytes 文件字節(jié)數(shù)組
     * @param fileName 文件名(輔助檢測(cè))
     * @return 真實(shí)MIME類型
     */
    public String detectFileByBytes(byte[] bytes, String fileName) {
        if (bytes == null || bytes.length == 0) {
            throw new IllegalArgumentException("字節(jié)數(shù)組不能為空");
        }

        return tika.detect(bytes, Metadata.TIKA_MIME_FILE);
    }

    /**
     * 3. 基于擴(kuò)展名檢測(cè)(僅作輔助,準(zhǔn)確率低)
     * @param fileName 文件名(如test.pdf)
     * @return 推測(cè)的MIME類型
     */
    public String detectFileByExtension(String fileName) {
        return tika.detect(fileName);
    }
}

功能 2:文件內(nèi)容與元數(shù)據(jù)提取

@Service
public class TikaContentExtractService {

    private final Parser autoDetectParser;

    // 注入自動(dòng)檢測(cè)解析器
    public TikaContentExtractService(Parser autoDetectParser) {
        this.autoDetectParser = autoDetectParser;
    }

    /**
     * 提取文件的文本內(nèi)容(支持PDF、Word、Excel等)
     * @param file 上傳的文件
     * @return 提取的純文本
     */
    public String extractText(MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("文件不能為空");
        }

        // 1. 內(nèi)容處理器:BodyContentHandler用于接收文本內(nèi)容,設(shè)置容量(避免大文件OOM)
        ContentHandler contentHandler = new BodyContentHandler(10 * 1024 * 1024); // 10MB上限

        // 2. 元數(shù)據(jù):存儲(chǔ)文件的結(jié)構(gòu)化信息
        Metadata metadata = new Metadata();
        metadata.add(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());

        // 3. 解析上下文:用于傳遞解析器所需的額外信息(如密碼,適用于加密文件)
        ParseContext parseContext = new ParseContext();
        parseContext.set(Parser.class, autoDetectParser); // 綁定當(dāng)前解析器

        // 4. 解析文件并提取內(nèi)容
        try (InputStream inputStream = file.getInputStream()) {
            autoDetectParser.parse(inputStream, contentHandler, metadata, parseContext);
            return contentHandler.toString();
        }
    }

    /**
     * 提取文件的元數(shù)據(jù)(如創(chuàng)建時(shí)間、作者、文件大?。?     * @param file 上傳的文件
     * @return 元數(shù)據(jù)鍵值對(duì)(格式化輸出)
     */
    public String extractMetadata(MultipartFile file) throws Exception {
        Metadata metadata = new Metadata();
        ParseContext parseContext = new ParseContext();
        parseContext.set(Parser.class, autoDetectParser);

        try (InputStream inputStream = file.getInputStream()) {
            autoDetectParser.parse(inputStream, new BodyContentHandler(), metadata, parseContext);
        }

        // 格式化元數(shù)據(jù)輸出(遍歷所有元數(shù)據(jù)鍵)
        StringBuilder metadataStr = new StringBuilder();
        for (String name : metadata.names()) {
            metadataStr.append(name).append(": ").append(metadata.get(name)).append("\n");
        }
        return metadataStr.toString();
    }


    /**
     * 提取加密文件的文本內(nèi)容(支持PDF、Word、Excel等)
     * @param file 上傳的文件
     * @return 提取的純文本
     * 1. 對(duì)舊版 Office 文檔(.doc、.xls),使用 POI 的 EncryptionInfo 和 Decryptor 直接解密
     * 2. 對(duì)新版 Office 文檔(.docx、.xlsx),仍使用 Tika 的 PasswordProvider
     */
    public String extractEncryptedText(MultipartFile file, String password) throws Exception {
        ContentHandler handler = new BodyContentHandler();
        Metadata metadata = new Metadata();
        metadata.add(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());
        metadata.set("password", password);

        AutoDetectParser autoDetectParser = new AutoDetectParser();
        ParseContext context = new ParseContext();
        context.set(PasswordProvider.class,metadata1 -> password);
        try (InputStream inputStream = file.getInputStream()) {
            autoDetectParser.parse(inputStream, handler, metadata, context);
            return handler.toString();
        }
    }
}

功能 3:自定義解析器

// 自定義解析器:處理.csv格式文件(示例,Tika已內(nèi)置CSV解析器,此處僅演示擴(kuò)展)
public class CustomCsvParser extends AbstractParser {

    // 聲明支持的MIME類型
    @Override
    public Set<MediaType> getSupportedTypes(ParseContext context) {
        return Collections.singleton(MediaType.parse("text/csv"));
    }

    // 核心解析邏輯
    @Override
    public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context)
            throws IOException, SAXException {
        // 1. 設(shè)置CSV文件的元數(shù)據(jù)
        metadata.add("file_format", "CSV");
        metadata.add("delimiter", ",");

        // 2. 讀取CSV內(nèi)容并寫入ContentHandler
        String csvContent = readInputStreamAsString(stream);
        handler.characters(csvContent.toCharArray(), 0, csvContent.length());
    }

    private String readInputStreamAsString(InputStream inputStream) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int nRead;
        byte[] data = new byte[1024];
        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }
        buffer.flush();
        return new String(buffer.toByteArray(), StandardCharsets.UTF_8);
    }
}

測(cè)試代碼

圖片圖片

@Test
    public void tikaFile() throws Exception {
        File fakeImageFile = new File("D:\\文檔\\自動(dòng)巡檢腳本部署說(shuō)明.docx");
        MultipartFile mockFile = new MockMultipartFile("file",new FileInputStream(fakeImageFile));
        System.out.println(tikaContentExtractService.extractText(mockFile));
        System.out.println(tikaContentExtractService.extractMetadata(mockFile));
//        System.out.println(tikaContentExtractService.extractEncryptedText(mockFile,"123456"));
    }


責(zé)任編輯:武曉燕 來(lái)源: 一安未來(lái)
相關(guān)推薦

2025-01-02 11:01:13

Apache文件類型部署

2025-03-31 08:43:34

SpringTika優(yōu)化

2025-09-10 09:31:04

2017-04-17 10:35:40

Spring BooRedis 操作

2009-11-16 12:17:46

PHP上傳文件類型

2022-08-24 08:42:59

Minio存儲(chǔ)Golang

2024-10-14 13:26:42

2025-09-08 04:07:00

SpringApache工具

2024-10-31 09:42:08

2009-08-12 16:44:13

.NET文件類型

2023-10-12 10:32:51

2010-03-10 09:27:36

Linux鏈接文件類型

2010-01-05 15:32:33

2010-03-01 15:34:38

Linux文件類型

2025-03-26 03:25:00

SpringGuavaCaffeine

2024-11-11 10:02:37

Spring搜索數(shù)據(jù)

2025-07-02 07:33:02

Spring倒排索引分布式

2022-12-23 08:28:42

策略模式算法

2025-05-09 07:20:02

Spring數(shù)據(jù)庫(kù)檢索

2009-10-21 10:50:59

Linux文件類型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)