How to write data to two java.io.OutputStream objects at once?

26,470

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.

Share:
26,470
daveslab
Author by

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, 2020

Comments

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

    Thanks!

  • Steve McLeod
    Steve McLeod over 12 years
    Nothing in I/O is ever trivial. Even if it seems so at first.
  • Dave Newton
    Dave Newton over 12 years
    The writes should be synchronized.
  • user207421
    user207421 over 12 years
    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.
  • Kohányi Róbert
    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
    rurouniwallace about 11 years
    This doesn't really answer the question of how to write to all of them simultaneous.
  • rurouniwallace
    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
    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
    Kevin about 11 years
    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
    Kevin about 11 years
    Just checked, TOS is indeed in series, check the source here
  • Kevin
    Kevin about 11 years
    source: @override public synchronized void write(final int b) throws IOException { super.write(b); this.branch.write(b); }
  • rurouniwallace
    rurouniwallace about 11 years
    I see...so then what is the advantage of using TeeOutputStream over simply iterating over each output stream yourself?
  • Kevin
    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
    Raj about 10 years
    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)
  • Whome
    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
    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
    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 an IOException and tee.close() is skipped because exception handling did not ensure the second stream got closed.
  • Kohányi Róbert
    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
    Dave Newton over 5 years
    @Raj Because an OutputStream is not a PrintStream but the other way around.