La manera más eficiente de crear InputStream desde OutputStream

Esta página: http://blog.ostermiller.org/convert-java-outputstreaminputstream describe cómo crear un InputStream desde OutputStream:

new ByteArrayInputStream(out.toByteArray()) 

Otras alternativas son usar PipedStreams y nuevos subprocesos, lo cual es engorroso.

No me gusta la idea de copiar muchos megabytes a una matriz de bytes de memoria nueva. ¿Hay una biblioteca que hace esto de manera más eficiente?

EDITAR:

Por consejo de Laurence Gonsalves, probé PipedStreams y resultó que no son tan difíciles de tratar. Aquí está el código de muestra en clojure:

 (defn #^PipedInputStream create-pdf-stream [pdf-info] (let [in-stream (new PipedInputStream) out-stream (PipedOutputStream. in-stream)] (.start (Thread. #(;Here you write into out-stream))) in-stream)) 

Si no quiere copiar todos los datos en un búfer en memoria de una sola vez, entonces tendrá que tener su código que usa OutputStream (el productor) y el código que usa InputStream (el consumidor). ) Alternar en el mismo hilo, o operar al mismo tiempo en dos hilos separados. Tenerlos operando en el mismo hilo es probablemente mucho más complicado que usar dos hilos separados, es mucho más propenso a errores (tendrá que asegurarse de que el consumidor nunca bloquee la espera de la entrada, o efectivamente se estancará) y necesitaría tener el productor y el consumidor ejecutándose en el mismo ciclo, que parece estar demasiado unido.

Entonces usa un segundo hilo Realmente no es tan complicado. La página a la que vinculó tenía un ejemplo perfecto:

  PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in); new Thread( new Runnable(){ public void run(){ class1.putDataOnOutputStream(out); } } ).start(); class2.processDataFromInputStream(in); 

Hay otra biblioteca de código abierto llamada EasyStream que trata con tuberías e hilo de una manera transparente. Eso no es realmente complicado si todo va bien. Los problemas surgen cuando (mirando el ejemplo de Laurence Gonsalves)

class1.putDataOnOutputStream (out);

Lanza una excepción. En ese ejemplo, el hilo simplemente se completa y la excepción se pierde, mientras que el InputStream externo se puede truncar.

Easystream trata con la propagación de excepciones y otros problemas desagradables que he estado depurando durante aproximadamente un año. (Soy el administrador de la biblioteca: obviamente mi solución es la mejor;)) Aquí hay un ejemplo sobre cómo usarlo:

 final InputStreamFromOutputStream isos = new InputStreamFromOutputStream(){ @Override public String produce(final OutputStream dataSink) throws Exception { /* * call your application function who produces the data here * WARNING: we're in another thread here, so this method shouldn't * write any class field or make assumptions on the state of the outer class. */ return produceMydata(dataSink) } }; 

También hay una buena introducción en la que se explican todas las demás formas de convertir un OutputStream en un InputStream. Vale la pena echar un vistazo.

Una solución simple que evita copiar el buffer es crear un ByteArrayOutputStream propósito especial:

 public class CopyStream extends ByteArrayOutputStream { public CopyStream(int size) { super(size); } /** * Get an input stream based on the contents of this output stream. * Do not use the output stream after calling this method. * @return an {@link InputStream} */ public InputStream toInputStream() { return new ByteArrayInputStream(this.buf, 0, this.count); } } 

Escriba en la secuencia de salida anterior según sea necesario, luego llame a toInputStream para obtener una secuencia de entrada sobre el búfer subyacente. Considere el flujo de salida como cerrado después de ese punto.

Creo que la mejor manera de conectar InputStream a un OutputStream es a través de transmisiones canalizadas , disponible en el paquete java.io, de la siguiente manera:

 // 1- Define stream buffer private static final int PIPE_BUFFER = 2048; // 2 -Create PipedInputStream with the buffer public PipedInputStream inPipe = new PipedInputStream(PIPE_BUFFER); // 3 -Create PipedOutputStream and bound it to the PipedInputStream object public PipedOutputStream outPipe = new PipedOutputStream(inPipe); // 4- PipedOutputStream is an OutputStream, So you can write data to it // in any way suitable to your data. for example: while (Condition) { outPipe.write(mByte); } /*Congratulations:D. Step 4 will write data to the PipedOutputStream which is bound to the PipedInputStream so after filling the buffer this data is available in the inPipe Object. Start reading it to clear the buffer to be filled again by the PipedInputStream object.*/ 

En mi opinión, hay dos ventajas principales para este código:

1 – No hay consumo adicional de memoria excepto para el buffer.

2 – No necesita manejar las colas de datos manualmente

Por lo general, trato de evitar crear un hilo por separado debido a la mayor posibilidad de estancamiento, la mayor dificultad para comprender el código y los problemas de tratar con las excepciones.

Aquí está mi solución propuesta: un ProducerInputStream que crea contenido en fragmentos mediante llamadas repetidas a producirChunk ():

 public abstract class ProducerInputStream extends InputStream { private ByteArrayInputStream bin = new ByteArrayInputStream(new byte[0]); private ByteArrayOutputStream bout = new ByteArrayOutputStream(); @Override public int read() throws IOException { int result = bin.read(); while ((result == -1) && newChunk()) { result = bin.read(); } return result; } @Override public int read(byte[] b, int off, int len) throws IOException { int result = bin.read(b, off, len); while ((result == -1) && newChunk()) { result = bin.read(b, off, len); } return result; } private boolean newChunk() { bout.reset(); produceChunk(bout); bin = new ByteArrayInputStream(bout.toByteArray()); return (bout.size() > 0); } public abstract void produceChunk(OutputStream out); }