How to write data to two java.io.OutputStream objects at once?
Solution 1
Try the Apache Commons TeeOutputStream.
Solution 2
Just roll your own. There isn't any magic at all. Using Apache's TeeOutputStream you would basically use the code below. Of course using the Apache Commons I/O library you can leverage other classes, but sometimes it is nice to actually write something for yourself. :)
public final class TeeOutputStream extends OutputStream {
private final OutputStream out;
private final OutputStream tee;
public TeeOutputStream(OutputStream out, OutputStream tee) {
if (out == null)
throw new NullPointerException();
else if (tee == null)
throw new NullPointerException();
this.out = out;
this.tee = tee;
}
@Override
public void write(int b) throws IOException {
out.write(b);
tee.write(b);
}
@Override
public void write(byte[] b) throws IOException {
out.write(b);
tee.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
tee.write(b, off, len);
}
@Override
public void flush() throws IOException {
out.flush();
tee.flush();
}
@Override
public void close() throws IOException {
try {
out.close();
} finally {
tee.close();
}
}
}
Testing with the above class with the following
public static void main(String[] args) throws IOException {
TeeOutputStream out = new TeeOutputStream(System.out, System.out);
out.write("Hello world!".getBytes());
out.flush();
out.close();
}
would print Hello World!Hello World!
.
(Note: the overridden close()
could use some care tho' :)
Solution 3
Just found this thread beacause I had to face the same problem. If someone wants to see my solution (java7 code):
package Core;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class MultiOutputStream extends OutputStream {
private List<OutputStream> out;
public MultiOutputStream(List<OutputStream> outStreams) {
this.out = new LinkedList<OutputStream>();
for (Iterator<OutputStream> i = outStreams.iterator(); i.hasNext();) {
OutputStream outputStream = (OutputStream) i.next();
if(outputStream == null){
throw new NullPointerException();
}
this.out.add(outputStream);
}
}
@Override
public void write(int arg0) throws IOException {
for (Iterator<OutputStream> i = out.iterator(); i.hasNext();) {
OutputStream var = (OutputStream) i.next();
var.write(arg0);
}
}
@Override
public void write(byte[] b) throws IOException{
for (Iterator<OutputStream> i = out.iterator(); i.hasNext();) {
OutputStream var = (OutputStream) i.next();
var.write(b);
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException{
for (Iterator<OutputStream> i = out.iterator(); i.hasNext();) {
OutputStream var = (OutputStream) i.next();
var.write(b, off, len);
}
}
@Override
public void close() throws IOException{
for (Iterator<OutputStream> i = out.iterator(); i.hasNext();) {
OutputStream var = (OutputStream) i.next();
var.close();
}
}
@Override
public void flush() throws IOException{
for (Iterator<OutputStream> i = out.iterator(); i.hasNext();) {
OutputStream var = (OutputStream) i.next();
var.flush();
}
}
}
Works fine so far, just tested some basic operation, e.g. setting up a MultiOutputStream from the System.out Stream and 2 PrintStreams each writing into a seperate log. I used
System.setOut(multiOutputStream);
to write to my terminal screen and two logs which worked without any problems.
Solution 4
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
final FileOutputStream fileStream = new FileOutputStream(new File("/tmp/somefile"));
OutputStream outStream = new OutputStream() {
public void write(int b) throws IOException {
byteStream.write(b);
fileStream.write(b);
}
};
outStream.write("Hello world".getBytes());
Solution 5
Roll your own, it's basically trivial. Use an ArrayList<OutputStream>
or whatever's popular nowadays to store all the streams you want and write the write
method to loop over all of them, writing to each.
daveslab
An ex-junior web developer, trained as Unix/C/C++ developer, and currently trying to be a Java/Python/Oracle nut. Programmer enthusiast, and general hacker with a fond affinity for asking and answering questions.
Updated on April 26, 2020Comments
-
daveslab about 4 years
I'm looking for magical Java class that will allow me to do something like this:
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); FileOutputStream fileStream = new FileOutputStream(new File("/tmp/somefile")); MultiOutputStream outStream = new MultiOutputStream(byteStream, fileStream); outStream.write("Hello world".getBytes());
Basically, I want
tee
forOutputStream
s in Java. Any ideas?Thanks!
-
Steve McLeod over 12 yearsNothing in I/O is ever trivial. Even if it seems so at first.
-
Dave Newton over 12 yearsThe writes should be synchronized.
-
user207421 over 12 yearsYou can simplify that. Have it extend
FilterOutputStream
; construct it withsuper(out)
; get rid of theout
member; and change all theout.
references tosuper.
references. -
Kohányi Róbert over 12 years@Dave Depends on the use case. EJP I like it better this way, but that would work also.
-
rurouniwallace about 11 yearsThis doesn't really answer the question of how to write to all of them simultaneous.
-
rurouniwallace about 11 years@Kevin No thanks. A for-each loop does not solve the problem of how to write them simultaneously. A for-each will write to the streams in sequence, they won't be written in parallel (i.e. simultaneously).
-
Kevin about 11 years@ZettaSuro So spawn them in threads, but that would not be any faster if the files are on the same physical disk, and potentially slower, and I'm quite sure that's not what the OP meant. It does write all the streams in one method call, which is the problem at hand.
-
Kevin about 11 yearsI'd bet quite a bit that if I look at the Apache
TeeOutputStream
source, it does them in series like this, not in parallel in separate threads. -
Kevin about 11 yearsJust checked, TOS is indeed in series, check the source here
-
Kevin about 11 yearssource:
@override public synchronized void write(final int b) throws IOException { super.write(b); this.branch.write(b); }
-
rurouniwallace about 11 yearsI see...so then what is the advantage of using TeeOutputStream over simply iterating over each output stream yourself?
-
Kevin about 11 years@ZettaSuro A single method call instead of a loop every time. Set up the stream once to write to all the streams you want, then just
teeos.write(stuff)
. -
Raj about 10 yearsi tried to used your code, however its giving me a compilation error - The method setOut(PrintStream) in the type System is not applicable for the arguments (MultiOutputStream)
-
Whome about 7 years@DaveNewton Mind explaining your comment about "writes should be synchronized", is it mandatory? Do regularl java io streams use a synchronized keyword?
-
Dave Newton about 7 years@Whome No, they don't. The issue here (as I saw it five years ago, anyway) is that since there are multiple writes, you'd probably want them to execute as a single unit to avoid potentially weird output result ordering. Could be wrong, though.
-
neuralmer about 6 years@KohányiRóbert There is something magical. Using code that lots of other people are using increases the likelihood that any bugs in the code are found and fixed. For example, when
out.close()
throws anIOException
andtee.close()
is skipped because exception handling did not ensure the second stream got closed. -
Kohányi Róbert about 6 years@neuralmer Hey, you're right about the close method. I've updated the code snippet just for reference. I still believe there's nothing magical about it though :)
-
Dave Newton over 5 years@Raj Because an
OutputStream
is not aPrintStream
but the other way around.