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!

daveslab
  • 10,000
  • 21
  • 60
  • 86

5 Answers5

46

Try the Apache Commons TeeOutputStream.

WilQu
  • 7,131
  • 6
  • 30
  • 38
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
26

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

Kohányi Róbert
  • 9,791
  • 4
  • 52
  • 81
  • The writes should be synchronized. – Dave Newton Nov 02 '11 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 Nov 03 '11 at 00:19
  • @Dave Depends on the use case. EJP I like it better this way, but that would work also. – Kohányi Róbert Nov 03 '11 at 07:49
  • @DaveNewton Mind explaining your comment about "writes should be synchronized", is it mandatory? Do regularl java io streams use a synchronized keyword? – Whome Mar 16 '17 at 14:16
  • @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. – Dave Newton Mar 16 '17 at 14:43
  • 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 Apr 24 '18 at 18:30
  • @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 :) – Kohányi Róbert Apr 25 '18 at 10:42
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());
Daniel De León
  • 13,196
  • 5
  • 87
  • 72
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.

  • 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 Feb 20 '14 at 10:32
  • @Raj Because an `OutputStream` is not a `PrintStream` but the other way around. – Dave Newton Aug 04 '18 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.

Kevin
  • 53,822
  • 15
  • 101
  • 132
  • 18
    Nothing in I/O is ever trivial. Even if it seems so at first. – Steve McLeod Nov 02 '11 at 21:17
  • This doesn't really answer the question of how to write to all of them simultaneous. – rurouniwallace Feb 24 '13 at 05: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). – rurouniwallace Feb 24 '13 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 Feb 24 '13 at 17:32
  • I'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 Feb 24 '13 at 17:33
  • Just checked, TOS is indeed in series, check the source [here](http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/output/TeeOutputStream.java?view=co) – Kevin Feb 24 '13 at 17:43
  • source: `@override public synchronized void write(final int b) throws IOException { super.write(b); this.branch.write(b); }` – Kevin Feb 24 '13 at 17:44
  • I see...so then what is the advantage of using TeeOutputStream over simply iterating over each output stream yourself? – rurouniwallace Feb 24 '13 at 17:47
  • 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 Feb 24 '13 at 17:47