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

如何防止DocumentBuilderFactory调用parse方法时自动关闭底层InputStream

问题根因

  1. 流内容已提前耗尽
    你在调用builder.parse方法前,已经执行了batchFileUtils.fileToByteArray(inputStream),该方法会读取输入流的全部字节,将流的读取指针移动到末尾。即使后续流没有被关闭,再次读取也只能得到空内容,这是你第二次调用fileToByteArray得到空结果的核心原因,和parse是否关闭流没有直接关联。
  2. 自定义不可关闭流实现错误
    你重写的close方法逻辑存在问题:当canBeClosed为true时,你调用的是super.close(),而父类InputStream的close是默认空实现,根本不会关闭你持有的底层input流。同时你只代理了read()的无参方法,没有覆盖read的重载方法、skip、available等方法,也可能引发未知问题。

解决方案

1. 修复自定义不可关闭输入流

正确的代理实现需要把所有InputStream的方法都代理到底层输入流,close方法仅在允许时关闭底层流:

import java.io.IOException;
import java.io.InputStream;

public class UncloseableInputStream extends InputStream  {

    private final InputStream input;
    private boolean canBeClosed = false;
     
    public UncloseableInputStream(InputStream input) {
        this.input = input;
    }

    @Override
    public void close() throws IOException {    
        if(canBeClosed) {
            // 关闭底层流,而非调用父类的空close方法
            input.close();
        }
    }
    
    @Override
    public int read() throws IOException {
        return input.read();
    }

    @Override
    public int read(byte[] b) throws IOException {
        return input.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return input.read(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
        return input.skip(n);
    }

    @Override
    public int available() throws IOException {
        return input.available();
    }

    @Override
    public synchronized void mark(int readlimit) {
        input.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        input.reset();
    }

    @Override
    public boolean markSupported() {
        return input.markSupported();
    }

    public void allowToBeClosed() { 
        canBeClosed = true; 
    }
}

使用修复后的包装类传入builder.parse,即可阻止parse方法关闭底层的原始输入流。

2. 解决流提前耗尽问题

如果需要多次读取输入流内容,推荐直接复用第一次读取到的字节数组,避免重复读取原始流:

builderFactory = DocumentBuilderFactory.newInstance();
builder = builderFactory.newDocumentBuilder();
byte[] bytesFile = null;
try {
    bytesFile = batchFileUtils.fileToByteArray(inputStream);
    System.out.println("ok");
} catch (IOException e) {
    e.printStackTrace();
}
// 用字节数组新建流传入parse,不会影响原始流
xmlDocument = builder.parse(new UncloseableInputStream(new ByteArrayInputStream(bytesFile)));
// 后续需要使用流时,直接用字节数组重新构造即可
try {
    byte[] newBytes = batchFileUtils.fileToByteArray(new ByteArrayInputStream(bytesFile));
    System.out.println("ok");
} catch (IOException e) {
    e.printStackTrace();
}
// 所有操作完成后再关闭原始输入流
inputStream.close();

如果必须复用同一个原始输入流,可以先给原始流套上BufferedInputStream,它支持mark/reset操作,第一次读取前标记流的长度,读取完成后调用reset即可将指针复位到起始位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 20:36:06