How to define a method for reading all InputStreams including ZipInputStream? - java

I asked this once before and my post was deleted for not providing the code that uses the helper class. This time I have created a full test suite which shows the exact problem.
I am of the opinion that Java's ZipInputStream breaks the Liskov Substitution Principle (LSP) with regards to the InputStream abstract class. For ZipInputStream to be a subtype of InputStream, then objects of type InputStream in a program may be replaced with objects of type ZipInputStream without altering any of the desirable properties of that program (correctness, task performed, etc.).
The way in which LSP is violated here is for the read methods.
InputStream.read(byte[], int, int) states that it returns:
the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached.
The problem with ZipInputStream is that it has modified the meaning of a -1 return value. It states:
the actual number of bytes read, or -1 if the end of the entry is reached
(there is actually a hint to a similar problem with the available method in the Android documentation http://developer.android.com/reference/java/util/zip/ZipInputStream.html)
Now for the code that demonstrates the problem. (This is a cut down version of what I was actually trying to do so please excuse any poor style, multithreading problems, or the fact that the stream is advanced etc.).
Class that accepts any InputStream to generate a SHA1 of the stream:
public class StreamChecker {
private byte[] lastHash = null;
public boolean isDifferent(final InputStream inputStream) throws IOException {
final byte[] hash = generateHash(inputStream);
final byte[] temp = lastHash;
lastHash = hash;
return !Arrays.equals(temp, hash);
}
private byte[] generateHash(final InputStream inputStream) throws IOException {
return DigestUtils.sha1(inputStream);
}
}
Unit tests:
public class StreamCheckerTest {
#Test
public void testByteArrayInputStreamIsSame() throws IOException {
final StreamChecker checker = new StreamChecker();
final byte[] bytes = "abcdef".getBytes();
try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
Assert.assertTrue(checker.isDifferent(stream));
}
try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
Assert.assertFalse(checker.isDifferent(stream));
}
// Passes
}
#Test
public void testByteArrayInputStreamWithDifferentDataIsDifferent() throws IOException {
final StreamChecker checker = new StreamChecker();
byte[] bytes = "abcdef".getBytes();
try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
Assert.assertTrue(checker.isDifferent(stream));
}
bytes = "123456".getBytes();
try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
Assert.assertTrue(checker.isDifferent(stream));
}
// Passes
}
#Test
public void testZipInputStreamIsSame() throws IOException {
final StreamChecker checker = new StreamChecker();
final byte[] bytes = "abcdef".getBytes();
try (final ZipInputStream stream = createZipStream("test", bytes)) {
Assert.assertTrue(checker.isDifferent(stream));
}
try (final ZipInputStream stream = createZipStream("test", bytes)) {
Assert.assertFalse(checker.isDifferent(stream));
}
// Passes
}
#Test
public void testZipInputStreamWithDifferentEntryDataIsDifferent() throws IOException {
final StreamChecker checker = new StreamChecker();
byte[] bytes = "abcdef".getBytes();
try (final ZipInputStream stream = createZipStream("test", bytes)) {
Assert.assertTrue(checker.isDifferent(stream));
}
bytes = "123456".getBytes();
try (final ZipInputStream stream = createZipStream("test", bytes)) {
// Fails here
Assert.assertTrue(checker.isDifferent(stream));
}
}
private ZipInputStream createZipStream(final String entryName,
final byte[] bytes) throws IOException {
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final ZipOutputStream stream = new ZipOutputStream(outputStream)) {
stream.putNextEntry(new ZipEntry(entryName));
stream.write(bytes);
return new ZipInputStream(new ByteArrayInputStream(
outputStream.toByteArray()));
}
}
}
So back to the problem... LSP is violated since you can read to the end of the stream for an InputStream but not for a ZipInputStream and of course this will break the correctness property of any method that tries to use it in such a way.
Is there any way that this can be achieved or is ZipInputStream fundamentally flawed?

I see no LSP violation. The documentation for ZipInputStream.read(byte[], int, int) says 'Reads from the current ZIP entry into an array of bytes'.
At any one time, the ZipInputStream is really the input stream of the entry, not the whole ZIP file. And it's hard to see what else ZipInputStream.read() could possibly do at end of entry other than return -1.
this will break the correctness property of any method that tries to use it in such a way
Hard to see how the method would ever know.

Related

How to write out percentage of file copying using Binary Stream?

I want to show the percentage while copying file by using binary stream but I don't know the way, that How to do it?
Below is my code.
public static void binaryStream() throws IOException {
try {
FileInputStream inputStream = new FileInputStream(new File("Untitled.png"));
FileOutputStream outputStream = new FileOutputStream(new File("Untitled-copied.png"));
int data;
while ((data = inputStream.read()) >= 0) {
outputStream.write(data);
}
outputStream.write(data);
inputStream.close();
outputStream.close();
} catch (FileNotFoundException e) {
System.out.println("Error");
} catch (IOException e) {
System.out.println("Error");
}
}
Example of how to do it like other people mentioned in the comments.
import java.io.*;
public class BinaryStream {
public static void binaryStream(String file1, String file2) throws Exception
{
File sourceFile = new File(file1);
try(
FileInputStream inputStream = new FileInputStream(sourceFile);
FileOutputStream outputStream = new FileOutputStream(new File(file2))
) {
long lenOfFile = sourceFile.length();
long currentBytesWritten = 0;
int data;
while ((data = inputStream.read()) != -1) {
outputStream.write(data);
currentBytesWritten += 1;
System.out.printf("%2.2f%%%n",
100*((double)currentBytesWritten)/((double)lenOfFile));
}
}
}
public static void main(String args[]) throws Exception {
binaryStream("Untitled.png", "Untitled-copied.png");
}
}
Note that I've made some changes:
Removed the extra outputStream.write() call you had that was writing extra content incorrectly
Using try-with-resources idiom to close the streams you open even on exceptions
Throw the exceptions instead of catching, as you shouldn't catch them if you can't handle them
Compare to -1, as that is the documented value for end of file (end of stream)
Output is like this on my computer:
0,06%
// removed data
99,89%
99,94%
100,00%
Note also that this code will print something after each byte written, so it is highly inefficient. You might want to do that less often. On that note, you're reading and writing one byte at a time, which is also very inefficient - you might want to use read(byte[]) instead, reading in chunks. Example of that, using 256 byte array:
import java.io.*;
public class BinaryStream {
public static void binaryStream(String file1, String file2) throws Exception {
File sourceFile = new File(file1);
try(
FileInputStream inputStream = new FileInputStream(sourceFile);
FileOutputStream outputStream = new FileOutputStream(new File(file2))
) {
long lenOfFile = sourceFile.length();
long bytesWritten = 0;
int amountOfBytesRead;
byte[] bytes = new byte[256];
while ((amountOfBytesRead = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, amountOfBytesRead);
bytesWritten += amountOfBytesRead;
System.out.printf("%2.2f%%%n",
100*((double)bytesWritten)/((double)lenOfFile));
}
}
}
public static void main(String args[]) throws Exception {
binaryStream("Untitled.png", "Untitled-copied.png");
}
}
Output on my computer:
14,69%
29,37%
44,06%
58,75%
73,44%
88,12%
100,00%
Note that in the first example, return value of .read() is actually the byte that was read, whereas in the second example, return value of .read() is the amount of bytes read and the actual bytes go into the byte array.

LZ4 is not fast compared to deflater compressing string

I tried to use LZ4 compression to compress a string object.But the result are not in favour to LZ4
Here is the program i tried
public class CompressionDemo {
public static byte[] compressGZIP(String data) throws IOException {
long start = System.nanoTime ();
ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length());
GZIPOutputStream gzip = new GZIPOutputStream(bos);
gzip.write(data.getBytes());
gzip.close();
byte[] compressed = bos.toByteArray();
bos.close();
System.out.println(System.nanoTime()-start);
return compressed;
}
public static byte[] compressLZ4(String data) throws IOException {
long start = System.nanoTime ();
LZ4Factory factory = LZ4Factory.fastestJavaInstance();
LZ4Compressor compressor = factory.highCompressor();
byte[] result = compressor.compress(data.getBytes());
System.out.println(System.nanoTime()-start);
return result;
}
public static byte[] compressDeflater(String stringToCompress) {
long start = System.nanoTime ();
byte[] returnValues = null;
try {
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
deflater.setInput(stringToCompress.getBytes("UTF-8"));
deflater.finish();
byte[] bytesCompressed = new byte[Short.MAX_VALUE];
int numberOfBytesAfterCompression = deflater.deflate(bytesCompressed);
returnValues = new byte[numberOfBytesAfterCompression];
System.arraycopy(bytesCompressed, 0, returnValues, 0, numberOfBytesAfterCompression);
} catch (Exception uee) {
uee.printStackTrace();
}
System.out.println(System.nanoTime()-start);
return returnValues;
}
public static void main(String[] args) throws IOException, DataFormatException {
System.out
.println("..it’s usually most beneficial to compress anyway, and determine which payload (the compressed or the uncompressed one) has the smallest size and include a small token to indicate whether decompression is required."
.getBytes().length);
byte[] arr = compressLZ4("..it’s usually most beneficial to compress anyway, and determine which payload (the compressed or the uncompressed one) has the smallest size and include a small token to indicate whether decompression is required.");
System.out.println(arr.length);
}
}
I have collected statics as above.But LZ4 is not that fast as stated
Please let me where am i doing wrong.
Your results are meaningless because the size before compression is too small. You are trying to measure the compression of a few thousand bytes at speeds over 100MB/s. The measurement is lost in the time taken by the JVM to warm up. Try again with an input file of several MBs. You should get numbers in line with my LZ4 implementation here: https://github.com/flanglet/kanzi/wiki/Compression-examples.

Inline input stream processing in Java

I need some help on below problem. I am working on a project where I need to deal with files.
I get the handle of input stream from the user from which before writing it to disk I need to perform certain steps.
calculate the file digest
check for only 1 zip file present, unzip the data if zipped
dos 2 unix conversion
record length validation
and encrypt and save the file to disk
Also need to break the flow if there is any exception in the process
I tried to use piped output and input stream, but the constraint is Java recommends it to run in 2 separate threads. Once I read from input stream I am not able to use it from other processing steps. Files can be very big so cannot cache all the data in buffer.
Please provide your suggestions or is there any third party lib I can use for same.
The biggest issue is that you'll need to peek ahead in the provided InputStream to decide if you received a zipfile or not.
private boolean isZipped(InputStream is) throws IOException {
try {
return new ZipInputStream(is).getNextEntry() != null;
} catch (final ZipException ze) {
return false;
}
}
After this you need to reset the inputstream to the initial position before setting up a DigestInputStream.
Then read a ZipInputstream or the DigestInputstream directly.
After you've done your processing, read the DigestInputStream to the end so you can obtain the digest.
Below code has been validated through a wrapping "CountingInputstream" that keeps track of the total number of bytes read from the provided FileInputStream.
final FileInputStream fis = new FileInputStream(filename);
final CountingInputStream countIs = new CountingInputStream(fis);
final boolean isZipped = isZipped(countIs);
// make sure we reset the inputstream before calculating the digest
fis.getChannel().position(0);
final DigestInputStream dis = new DigestInputStream(countIs, MessageDigest.getInstance("SHA-256"));
// decide which inputStream to use
InputStream is = null;
ZipInputStream zis = null;
if (isZipped) {
zis = new ZipInputStream(dis);
zis.getNextEntry();
is = zis;
} else {
is = dis;
}
final File tmpFile = File.createTempFile("Encrypted_", ".tmp");
final OutputStream os = new CipherOutputStream(new FileOutputStream(tmpFile), obtainCipher());
try {
readValidateAndWriteRecords(is, os);
failIf2ndZipEntryExists(zis);
} catch (final Exception e) {
os.close();
tmpFile.delete();
throw e;
}
System.out.println("Digest: " + obtainDigest(dis));
dis.close();
System.out.println("\nValidating bytes read and calculated digest");
final DigestInputStream dis2 = new DigestInputStream(new CountingInputStream(new FileInputStream(filename)), MessageDigest.getInstance("SHA-256"));
System.out.println("Digest: " + obtainDigest(dis2));
dis2.close();
Not really relevant, but these are the helper methods:
private String obtainDigest(DigestInputStream dis) throws IOException {
final byte[] buff = new byte[1024];
while (dis.read(buff) > 0) {
dis.read(buff);
}
return DatatypeConverter.printBase64Binary(dis.getMessageDigest().digest());
}
private void readValidateAndWriteRecords(InputStream is, final OutputStream os) throws IOException {
final BufferedReader br = new BufferedReader(new InputStreamReader(is));
// do2unix is done automatically by readline
for (String line = br.readLine(); line != null; line = br.readLine()) {
// record length validation
if (line.length() < 1) {
throw new RuntimeException("RecordLengthValidationFailed");
}
os.write((line + "\n").getBytes());
}
}
private void failIf2ndZipEntryExists(ZipInputStream zis) throws IOException {
if (zis != null && zis.getNextEntry() != null) {
throw new RuntimeException("Zip File contains multiple entries");
}
}
==> output:
Digest: jIisvDleAttKiPkyU/hDvbzzottAMn6n7inh4RKxPOc=
CountingInputStream closed. Total number of bytes read: 1100
Validating bytes read and calculated digest
Digest: jIisvDleAttKiPkyU/hDvbzzottAMn6n7inh4RKxPOc=
CountingInputStream closed. Total number of bytes read: 1072
Fun question, I may have gone overboard with my answer :)

Java - Error deserializing HashTable containing primitive type

I have serialized a HashTable<String,Object> object using an ObjectOutputStream. When serializing the object, I get no exception, but upon deserialization, the following exception occurs:
Exception in thread "main" java.io.InvalidClassException: java.lang.Long; local class
incompatible: stream classdesc serialVersionUID = 4290774032661291999, local class
serialVersionUID = 4290774380558885855
I no longer get the error when I remove all of the keys in the HashTable that have a value that is not a String (all of the key / value pairs I removed had a primitive type as their value).
What could be causing this error?
UPDATE - Here's the code
public static String serialize(Quiz quiz) throws IOException{
HashMap<String,Object> quizData = new HashMap<String,Object>();
quizData.put("version", 0); //int
quizData.put("name", quiz.getName()); //String
quizData.put("desc", quiz.getDesc()); //String
quizData.put("timelimitType", quiz.getTimelimitType()); //String
quizData.put("timelimit", quiz.getTimelimit()); //long
ArrayList<String> serializedQuestionsData = new ArrayList<String>();
for (Question question : quiz.getQuestions())
serializedQuestionsData.add(Question.serialize(question));
quizData.put("questions", serializedQuestionsData.toArray(new String[0])); //String[]
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos;
try { oos = new ObjectOutputStream(baos); } catch (IOException error){ throw error; }
try { oos.writeObject(quizData); } catch (IOException error){ throw error; }
return baos.toString();
}
#SuppressWarnings("unchecked")
public static Quiz deserialize(String serializedQuizData) throws IOException, ClassNotFoundException{
ByteArrayInputStream bais = new ByteArrayInputStream(serializedQuizData.getBytes());
ObjectInputStream ois;
try { ois = new ObjectInputStream(bais); } catch (IOException error){ throw error; }
HashMap<String,Object> quizData;
// Exception occurs on the following line!!
try { quizData = (HashMap<String,Object>) ois.readObject(); } catch (ClassNotFoundException error){ throw error; }
Quiz quiz;
if ((int) quizData.get("version") == 0){
quiz = new Quiz((String) quizData.get("name"),
(String) quizData.get("desc"),
(String) quizData.get("timelimitType"),
(long) quizData.get("timelimit"));
for (String serializedQuestionData : (String[]) quizData.get("questions"))
quiz.addQuestion(Question.deserialize(serializedQuestionData));
} else {
throw new UnsupportedOperationException("Unsupported version: \"" + quizData.get("version") + "\"");
}
return quiz;
}
The problem is that you're transforming a byte array output stream to a String using toString(). The toString() method simply uses the platform default encoding to transform the bytes (which do not represent characters at all but are purely binary data) into a String. This is thus a lossy operation, because your platform default encoding doesn't have a valid character for every possible byte.
You shouldn't use String to hold binary data. A String contains characters. If you really need a String, then encode the byte array using a Hexadecimal or Base64 encoder. Otherwise, simply use a byte array to hold your binary data:
public static byte[] serialize(Quiz quiz) throws IOException{
...
ByteArrayOutputStream baos = new ByteArrayOutputStream();
...
return baos.toByteArray();
}
#SuppressWarnings("unchecked")
public static Quiz deserialize(byte[] serializedQuizData) throws IOException, ClassNotFoundException{
ByteArrayInputStream bais = new ByteArrayInputStream(serializedQuizData);
...
return quiz;
}
The only explanation I can think of is that is that something is corrupting your object stream between you reading it and writing it. The serialVersionID in "the local class) (4290774380558885855) is standard across all Java implementations that try to be compatible with Java (tm). The source code for java.lang.Long says that that serial version id has not changed since Java 1.0.2.
If you need further help, you will need to provide an SSCCE that covers both creation and reading of the serialized object.

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";
}
}
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());
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();
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.
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.
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.
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)
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/
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.
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();
}
}
}
}
}
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);
}
}
}
}

Categories