How to clone an InputStream?

JavaCloneInputstream

Java Problem Overview


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

Java Solutions


Solution 1 - Java

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()); 

Solution 2 - Java

You want to use Apache's CloseShieldInputStream:

This is a wrapper that will prevent the stream from being closed. You'd do something like this.

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();

Solution 3 - Java

You can't clone it, and how you are going to solve your problem depends on what the source of the data is.

One solution is to read all data from the InputStream into a byte array, and then create a ByteArrayInputStream around that byte array, and pass that input stream into your method.

Edit 1: That is, if the other method also needs to read the same data. I.e you want to "reset" the stream.

Solution 4 - Java

If the data read from the stream is large, I would recommend using a TeeInputStream from Apache Commons IO. That way you can essentially replicate the input and pass a t'd pipe as your clone.

Solution 5 - Java

This might not work in all situations, but here is what I did: I extended the FilterInputStream class and do the required processing of the bytes as the external lib reads the data.

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
    }

}

Then you simply pass an instance of StreamBytesWithExtraProcessingInputStream where you would have passed in the input stream. With the original input stream as constructor parameter.

It should be noted that this works byte for byte, so don't use this if high performance is a requirement.

Solution 6 - Java

UPD. Check the comment before. It isn't exactly what was asked.

If you are using apache.commons you may copy streams using IOUtils .

You can use following code:

InputStream = IOUtils.toBufferedInputStream(toCopy);

Here is the full example suitable for your situation:

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

This code requires some dependencies:

MAVEN

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.4</version>
</dependency>

GRADLE

'commons-io:commons-io:2.4'

Here is the DOC reference for this method:

> Fetches entire contents of an InputStream and represent same data as > result InputStream. This method is useful where, > > Source InputStream is slow. It has network resources associated, so we > cannot keep it open for long time. It has network timeout associated. >

You can find more about IOUtils here: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)

Solution 7 - Java

Below is the solution with Kotlin.

You can copy your InputStream into ByteArray

val inputStream = ...

val byteOutputStream = ByteArrayOutputStream()
inputStream.use { input ->
    byteOutputStream.use { output ->
        input.copyTo(output)
    }
}

val byteInputStream = ByteArrayInputStream(byteOutputStream.toByteArray())

If you need to read the byteInputStream multiple times, call byteInputStream.reset() before reading again.

https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/

Solution 8 - Java

The class below should do the trick. Just create an instance, call the "multiply" method, and provide the source input stream and the amount of duplicates you need.

Important: you must consume all cloned streams simultaneously in separate threads.

package foo.bar;

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class InputStreamMultiplier {
	protected static final int BUFFER_SIZE = 1024;
	private ExecutorService executorService = Executors.newCachedThreadPool();

	public InputStream[] multiply(final InputStream source, int count) throws IOException {
		PipedInputStream[] ins = new PipedInputStream[count];
		final PipedOutputStream[] outs = new PipedOutputStream[count];

		for (int i = 0; i < count; i++)
		{
			ins[i] = new PipedInputStream();
			outs[i] = new PipedOutputStream(ins[i]);
		}

		executorService.execute(new Runnable() {
			public void run() {
				try {
					copy(source, outs);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});

		return ins;
	}

	protected void copy(final InputStream source, final PipedOutputStream[] outs) throws IOException {
		byte[] buffer = new byte[BUFFER_SIZE];
		int n = 0;
		try {
			while (-1 != (n = source.read(buffer))) {
				//write each chunk to all output streams
				for (PipedOutputStream out : outs) {
					out.write(buffer, 0, n);
				}
			}
		} finally {
			//close all output streams
			for (PipedOutputStream out : outs) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

Solution 9 - Java

Cloning an input stream might not be a good idea, because this requires deep knowledge about the details of the input stream being cloned. A workaround for this is to create a new input stream that reads from the same source again.

So using some Java 8 features this would look like this:

public class Foo {

    private Supplier<InputStream> inputStreamSupplier;

    public void bar() {
        procesDataThisWay(inputStreamSupplier.get());
        procesDataTheOtherWay(inputStreamSupplier.get());
    }
    
    private void procesDataThisWay(InputStream) {
        // ...
    }

    private void procesDataTheOtherWay(InputStream) {
        // ...
    }
}

This method has the positive effect that it will reuse code that is already in place - the creation of the input stream encapsulated in inputStreamSupplier. And there is no need to maintain a second code path for the cloning of the stream.

On the other hand, if reading from the stream is expensive (because a it's done over a low bandwith connection), then this method will double the costs. This could be circumvented by using a specific supplier that will store the stream content locally first and provide an InputStream for that now local resource.

Solution 10 - Java

Enhancing the @Anthony Accioly with the example.

> InputStream: Clones the bytes-Stream and provides number of copies as a List Collection.

public static List<InputStream> multiplyBytes(InputStream input, int cloneCount) throws IOException {
	List<InputStream> copies = new ArrayList<InputStream>();
	
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	copy(input, baos);
	
	for (int i = 0; i < cloneCount; i++) {
		copies.add(new ByteArrayInputStream(baos.toByteArray()));
	}
	return copies;
}
// IOException - If reading the Reader or Writing into the Writer goes wrong.
public static void copy(Reader in, Writer out) throws IOException {
	try {
		char[] buffer = new char[1024];
		int nrOfBytes = -1;
		while ((nrOfBytes = in.read(buffer)) != -1) {
			out.write(buffer, 0, nrOfBytes);
		}
		out.flush();
	} finally {
		close(in);
		close(out);
	}
}

> Reader: Clones the chars-Stream and provides number of copies as a List Collection.

public static List<Reader> multiplyChars(Reader reader, int cloneCOunt) throws IOException {
	List<Reader> copies = new ArrayList<Reader>();
	BufferedReader bufferedInput = new BufferedReader(reader);
	StringBuffer buffer = new StringBuffer();
	String delimiter = System.getProperty("line.separator");
	String line;
	while ((line = bufferedInput.readLine()) != null) {
		if (!buffer.toString().equals(""))
			buffer.append(delimiter);
		buffer.append(line);
	}
	close(bufferedInput);
	for (int i = 0; i < cloneCOunt; i++) {
		copies.add(new StringReader(buffer.toString()));
	}
	return copies;
}
public static void copy(InputStream in, OutputStream out) throws IOException {
	try {
		byte[] buffer = new byte[1024];
		int nrOfBytes = -1;
		while ((nrOfBytes = in.read(buffer)) != -1) {
			out.write(buffer, 0, nrOfBytes);
		}
		out.flush();
	} finally {
		close(in);
		close(out);
	}
}

Full Example:

public class SampleTest {

	public static void main(String[] args) throws IOException {
		String filePath = "C:/Yash/StackoverflowSSL.cer";
		InputStream fileStream = new FileInputStream(new File(filePath) );
		
		List<InputStream> bytesCopy = multiplyBytes(fileStream, 3);
		for (Iterator<InputStream> iterator = bytesCopy.iterator(); iterator.hasNext();) {
			InputStream inputStream = (InputStream) iterator.next();
			System.out.println("Byte Stream:"+ inputStream.available()); // Byte Stream:1784
		}
		printInputStream(bytesCopy.get(0));
		
		//java.sql.Clob clob = ((Clob) getValue(sql)); - clob.getCharacterStream();
		Reader stringReader = new StringReader("StringReader that reads Characters from the specified string.");
		List<Reader> charsCopy = multiplyChars(stringReader, 3);
		for (Iterator<Reader> iterator = charsCopy.iterator(); iterator.hasNext();) {
			Reader reader = (Reader) iterator.next();
			System.out.println("Chars Stream:"+reader.read()); // Chars Stream:83
		}
		printReader(charsCopy.get(0));
	}
	
	// Reader, InputStream - Prints the contents of the reader to System.out.
	public static void printReader(Reader reader) throws IOException {
		BufferedReader br = new BufferedReader(reader);
		String s;
		while ((s = br.readLine()) != null) {
			System.out.println(s);
		}
	}
	public static void printInputStream(InputStream inputStream) throws IOException {
		printReader(new InputStreamReader(inputStream));
	}

	// Closes an opened resource, catching any exceptions.
	public static void close(Closeable resource) {
		if (resource != null) {
			try {
				resource.close();
			} catch (IOException e) {
				System.err.println(e);
			}
		}
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRenato DinhaniView Question on Stackoverflow
Solution 1 - JavaAnthony AcciolyView Answer on Stackoverflow
Solution 2 - JavaFemiView Answer on Stackoverflow
Solution 3 - JavaKajView Answer on Stackoverflow
Solution 4 - JavaNathan RyanView Answer on Stackoverflow
Solution 5 - JavaDiederikView Answer on Stackoverflow
Solution 6 - JavaAndrey EView Answer on Stackoverflow
Solution 7 - JavaDesmond LuaView Answer on Stackoverflow
Solution 8 - Javavstrom coderView Answer on Stackoverflow
Solution 9 - JavaSpaceTruckerView Answer on Stackoverflow
Solution 10 - JavaYashView Answer on Stackoverflow