You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Apache Tika提取元数据时偶发ArrayIndexOutOfBoundsException问题求助

问题

使用Apache Tika提取文本、PDF文件内容时,调用metadata.names()方法偶发ArrayIndexOutOfBoundsException异常,出现概率约2-3%。

相关代码

public ExtractionResponse getText(InputStream sourceInputStream, int maxExtractedStringLength) {
    tika.setMaxStringLength(maxExtractedStringLength);
    Metadata metadata = new Metadata();

    try {
        Reader extractedContentReader = tika.parse(sourceInputStream, metadata);
        ReaderInputStream readerInputStream = ReaderInputStream.builder().setReader(extractedContentReader).get();
        return ExtractionResponse.of(readerInputStream, getMetadatas(metadata), StringUtils.EMPTY);
        
    } catch (IOException e) {
        log.info("Error in file extraction", e);
        throw new ApplicationRuntimeException(e);
    }
}

private String getMetadatas(Metadata metadata) throws IOException {
    Map<String, List<String>> metadataMap = Stream.of(metadata.names())
            .collect(Collectors.toMap(Function.identity(), name -> asList(metadata.getValues(name))));
    return JSONUtil.generateJSON(metadataMap);
}

异常栈信息

java.lang.ArrayIndexOutOfBoundsException: Index 14 out of bounds for length 14
at java.base/java.util.HashMap.keysToArray(HashMap.java:950)
at java.base/java.util.HashMap$KeySet.toArray(HashMap.java:997)
at org.apache.tika.metadata.Metadata.names(Metadata.java:156)
at com.myzone.extractors.TikaDocumentHandler.getMetadatas(TikaDocumentHandler.java:81)
at com.myzone.extractors.TikaDocumentHandler.getText(TikaDocumentHandler.java:54)
排查与解决

原因分析

从异常栈可以定位到问题出在HashMap.keysToArray方法,核心原因是Tika的Metadata内部依赖的HashMap存在并发修改:

  • 你的代码看似单线程调用,但Tika解析过程中可能启动异步线程修改Metadata;
  • 若多线程环境下共享了同一个Tika实例,会导致Metadata被并发读写;
  • 旧版本Tika(如1.x系列部分版本)的Metadata类未做线程安全处理,内部HashMap在并发场景下会触发数组越界这类并发异常。

解决方案

  1. 保证Tika实例的线程隔离
    Tika类本身不是线程安全的,禁止多线程共享同一个实例。可以用ThreadLocal维护每个线程的独立实例:
private ThreadLocal<Tika> tikaThreadLocal = ThreadLocal.withInitial(Tika::new);

public ExtractionResponse getText(InputStream sourceInputStream, int maxExtractedStringLength) {
    Tika tika = tikaThreadLocal.get();
    tika.setMaxStringLength(maxExtractedStringLength);
    // 后续解析逻辑不变
}
  1. 升级Tika版本至2.x系列
    Tika 2.x已重构Metadata实现,改用线程安全的数据结构,彻底解决了这类并发问题。如果当前使用1.x版本,直接升级到最新稳定版是最优解。

  2. 临时同步Metadata访问
    若暂时无法升级版本,可对Metadata的读写操作加锁,缓解并发冲突:

private String getMetadatas(Metadata metadata) throws IOException {
    synchronized (metadata) {
        Map<String, List<String>> metadataMap = Stream.of(metadata.names())
                .collect(Collectors.toMap(Function.identity(), name -> asList(metadata.getValues(name))));
        return JSONUtil.generateJSON(metadataMap);
    }
}

注意:此方案仅为临时缓解,若Tika内部异步修改Metadata,锁无法完全覆盖所有场景,优先推荐前两种方案。

内容的提问来源于stack exchange,提问作者user3212707

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:52:39