javacloneinputstream

How to clone an InputStream?


I have a InputStream that I pass to a method to do some processing. I will use the same InputStream in other method, but after the first processing, the InputStream appears be closed inside the method.

How I can clone the InputStream to send to the method that closes him? There is another solution?

EDIT: the methods that closes the InputStream is an external method from a lib. I dont have control about closing or not.

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";
    }
}

Solution

  • If all you want to do is read the same information more than once, and the input data is small enough to fit into memory, you can copy the data from your InputStream to a ByteArrayOutputStream.

    Then you can obtain the associated array of bytes and open as many "cloned" ByteArrayInputStreams as you like.

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
    // Code simulating the copy
    // You could alternatively use NIO
    // 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 recorded bytes
    // Can be repeated as many times as you wish
    InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); 
    InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); 
    

    But if you really need to keep the original stream open to receive new data, then you will need to track the external call to close(). You will need to prevent close() from being called somehow.

    UPDATE (2019):

    Since Java 9 the the middle bits can be replaced with InputStream.transferTo:

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    input.transferTo(baos);
    InputStream firstClone = new ByteArrayInputStream(baos.toByteArray()); 
    InputStream secondClone = new ByteArrayInputStream(baos.toByteArray());