如何克隆InputStream?

Ren*_*ani 152 java clone inputstream

我有一个InputStream,我传递给一个方法来做一些处理.我将在其他方法中使用相同的InputStream,但在第一次处理之后,InputStream似乎在方法内部关闭.

我如何克隆InputStream发送到关闭他的方法?有另一种解决方案吗?

编辑:关闭InputStream的方法是来自lib的外部方法.我无法控制关闭与否.

private String getContent(HttpURLConnection con) {
    InputStream content = null;
    String charset = "";
    try {
        content = con.getInputStream();
        CloseShieldInputStream csContent = new CloseShieldInputStream(content);
        charset = getCharset(csContent);            
        return  IOUtils.toString(content,charset);
    } catch (Exception e) {
        System.out.println("Error downloading page: " + e);
        return null;
    }
}

private String getCharset(InputStream content) {
    try {
        Source parser = new Source(content);
        return parser.getEncoding();
    } catch (Exception e) {
        System.out.println("Error determining charset: " + e);
        return "UTF-8";
    }
}
Run Code Online (Sandbox Code Playgroud)

Ant*_*oly 175

如果你想要做的就是多次读取相同的信息,并输入数据是足够小,以适应到内存中,你可以从你的数据复制InputStream到一个ByteArrayOutputStream.

然后,您可以获取相关的字节数组,并根据需要打开尽可能多的"克隆" ByteArrayInputStream.

ByteArrayOutputStream baos = new ByteArrayOutputStream();

// Fake code simulating the copy
// You can generally do better with nio if you need...
// And please, unlike me, do something about the Exceptions :D
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
    baos.write(buffer, 0, len);
}
baos.flush();

// Open new InputStreams using the recorded bytes
// Can be repeated as many times as you wish
InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); 
InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); 
Run Code Online (Sandbox Code Playgroud)

但是,如果您确实需要保持原始流开放以接收新数据,那么您将需要跟踪此外部close()方法并防止以某种方式调用它.

  • 该方法消耗与输入流的全部内容成比例的存储器.最好使用`TeeInputStream`,如[here](http://stackoverflow.com/questions/12107049/how-can-i-make-a-copy-of-a-bufferedreader)中的答案所述. (6认同)
  • IOUtils(来自apache commons)有一个复制方法,可以在代码中间进行缓冲区读/写操作. (2认同)

Fem*_*emi 30

你想使用Apache的CloseShieldInputStream:

这是一个阻止流关闭的包装器.你会做这样的事情.

InputStream is = null;

is = getStream(); //obtain the stream 
CloseShieldInputStream csis = new CloseShieldInputStream(is);

// call the bad function that does things it shouldn't
badFunction(csis);

// happiness follows: do something with the original input stream
is.read();
Run Code Online (Sandbox Code Playgroud)


Kaj*_*Kaj 9

您无法克隆它,以及您将如何解决问题取决于数据的来源.

一种解决方案是将InputStream中的所有数据读入字节数组,然后围绕该字节数组创建ByteArrayInputStream,并将该输入流传递给您的方法.

编辑1:也就是说,如果另一种方法也需要读取相同的数据.即你想"重置"流.


Nat*_*yan 8

如果从流中读取的数据很大,我建议使用Apache Commons IO中的TeeInputStream.这样你就可以基本上复制输入并传递一个t'd管道作为你的克隆.


Die*_*rik 5

这可能无法在所有情况下都起作用,但是这是我所做的:我扩展了FilterInputStream类,并在外部lib读取数据时对字节进行了必要的处理。

public class StreamBytesWithExtraProcessingInputStream extends FilterInputStream {

    protected StreamBytesWithExtraProcessingInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int readByte = super.read();
        processByte(readByte);
        return readByte;
    }

    @Override
    public int read(byte[] buffer, int offset, int count) throws IOException {
        int readBytes = super.read(buffer, offset, count);
        processBytes(buffer, offset, readBytes);
        return readBytes;
    }

    private void processBytes(byte[] buffer, int offset, int readBytes) {
       for (int i = 0; i < readBytes; i++) {
           processByte(buffer[i + offset]);
       }
    }

    private void processByte(int readByte) {
       // TODO do processing here
    }

}
Run Code Online (Sandbox Code Playgroud)

然后,您只需StreamBytesWithExtraProcessingInputStream在输入流中传递一个传递位置的实例。使用原始输入流作为构造函数参数。

应该注意的是,这逐字节工作,因此如果需要高性能,请不要使用它。


And*_*y E 5

更新。检查之前的评论。这不是完全被问到的。

如果您正在使用,apache.commons您可以使用IOUtils.

您可以使用以下代码:

InputStream = IOUtils.toBufferedInputStream(toCopy);
Run Code Online (Sandbox Code Playgroud)

以下是适合您情况的完整示例:

public void cloneStream() throws IOException{
    InputStream toCopy=IOUtils.toInputStream("aaa");
    InputStream dest= null;
    dest=IOUtils.toBufferedInputStream(toCopy);
    toCopy.close();
    String result = new String(IOUtils.toByteArray(dest));
    System.out.println(result);
}
Run Code Online (Sandbox Code Playgroud)

此代码需要一些依赖项:

行家

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

摇篮

'commons-io:commons-io:2.4'
Run Code Online (Sandbox Code Playgroud)

这是此方法的 DOC 参考:

获取 InputStream 的全部内容并表示与结果 InputStream 相同的数据。这种方法在以下情况下很有用,

源 InputStream 很慢。它有关联的网络资源,所以我们不能让它长时间保持打开状态。它具有关联的网络超时。

您可以IOUtils在此处找到更多信息:http : //commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)

  • 这不会*克隆*输入流,而只是缓冲它。那不一样;OP 想要重新读取(副本)相同的流。 (9认同)