43

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 for OutputStreams in Java. Any ideas?

Thanks!

6 Answers 6

47

Try the Apache Commons TeeOutputStream.

27

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' :)

7
  • The writes should be synchronized. Commented Nov 2, 2011 at 21:53
  • 1
    You can simplify that. Have it extend FilterOutputStream; construct it with super(out); get rid of the out member; and change all the out. references to super. references.
    – user207421
    Commented Nov 3, 2011 at 0:19
  • @Dave Depends on the use case. EJP I like it better this way, but that would work also. Commented Nov 3, 2011 at 7:49
  • @DaveNewton Mind explaining your comment about "writes should be synchronized", is it mandatory? Do regularl java io streams use a synchronized keyword?
    – Whome
    Commented Mar 16, 2017 at 14:16
  • 1
    @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 an IOException and tee.close() is skipped because exception handling did not ensure the second stream got closed.
    – neuralmer
    Commented Apr 24, 2018 at 18:30
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());
4

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.

2
  • i 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)
    – Raj
    Commented Feb 20, 2014 at 10:32
  • @Raj Because an OutputStream is not a PrintStream but the other way around. Commented Aug 4, 2018 at 23:58
2

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.

9
  • 18
    Nothing in I/O is ever trivial. Even if it seems so at first. Commented Nov 2, 2011 at 21:17
  • This doesn't really answer the question of how to write to all of them simultaneous. Commented Feb 24, 2013 at 5:28
  • 1
    @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). Commented Feb 24, 2013 at 17:13
  • @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
    Commented Feb 24, 2013 at 17:32
  • 1
    @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).
    – Kevin
    Commented Feb 24, 2013 at 17:47
0

Here's an example of TeeOutputStream usage (keep outputting stdout & stderr) and printing it to file:

public static void main(String[] args) throws IOException {

    System.setOut(new PrintStream(new TeeOutputStream(System.out, new PrintStream("out.log"))));
    System.setErr(new PrintStream(new TeeOutputStream(System.err, new PrintStream("err.log"))));
...

Not the answer you're looking for? Browse other questions tagged or ask your own question.