Pregunta

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

new ByteArrayInputStream(out.toByteArray())

Otras alternativas son usar PipedStreams y nuevos hilos que son engorrosos.

No me gusta la idea de copiar muchos megabytes a una nueva matriz de bytes de memoria. ¿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 manejar. 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))
¿Fue útil?

Solución

Si no desea copiar todos los datos en un búfer en memoria de una vez, tendrá que tener su código que usa OutputStream (el productor) y el código que usa InputStream (el consumidor) puede alternar en el mismo hilo u operar simultáneamente en dos hilos separados. Hacer que operen en el mismo hilo probablemente sea mucho más complicado que usar dos hilos separados, es mucho más propenso a errores (deberá asegurarse de que el consumidor nunca bloquee la espera de entrada, o usted ' (efectivamente, punto muerto) y requeriría que el productor y el consumidor se ejecuten en el mismo ciclo que parece estar demasiado estrechamente acoplado.

Entonces use un segundo hilo. Realmente no es tan complicado. La página a la que enlazó 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);

Otros consejos

Hay otra biblioteca de código abierto llamada EasyStream que se ocupa de tuberías y roscas de manera transparente. Eso no es realmente complicado si todo sale bien. Surgen problemas cuando (mirando el ejemplo de Laurence Gonsalves)

  

class1.putDataOnOutputStream (out);

Lanza una excepción. En ese ejemplo, el hilo simplemente se completa y se pierde la excepción, mientras que el InputStream externo podría truncarse.

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

final InputStreamFromOutputStream<String> isos = new InputStreamFromOutputStream<String>(){
 @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 donde todas las otras formas de convertir un OutputStream en un InputStream se explican. Vale la pena echarle un vistazo.

Una solución simple que evita copiar el búfer es crear un ByteArrayOutputStream de 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 la secuencia de salida como cerrada después de ese punto.

Creo que la mejor manera de conectar InputStream a un OutputStream es a través de secuencias canalizadas , disponibles 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 el búfer.

2 - No necesita manejar la cola de datos manualmente

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

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

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);

}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top