2009-08-04 13 views
73

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

new ByteArrayInputStream(out.toByteArray()) 

Otras alternativas son utilizar PipedStreams y nuevos temas que es engorroso.

no me gusta la idea de la copia de varios megabytes a los nuevos en el conjunto de bytes de memoria. ¿Hay una biblioteca que hace esto de manera más eficiente?

EDIT:

Por consejo de Laurence Gonsalves, probé PipedStreams y resultó que no son tan difíciles de tratar. Aquí está el código de ejemplo 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)) 

Respuesta

63

Si no desea copiar todos los datos en un búfer de memoria en todos a la vez, entonces vamos a tener que tener el código que utiliza el OutputStream (el productor) y el código que usa InputStream (el consumidor) se alternan en el mismo subproceso o operan simultáneamente en dos subprocesos separados. Tenerlos operan en el mismo hilo es probablemente mucho más complicado que el uso de dos hilos separados, es mucho más propenso a errores (que tendrá que asegurarse de que el consumidor nunca bloques de espera para la entrada, o te punto muerto con eficacia) y Sería necesario que el productor y el consumidor se ejecutaran en el mismo ciclo, que parece estar demasiado unido.

a fin de utilizar un segundo hilo. Realmente no es tan complicado. La página que tenía vinculado 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); 
+0

Creo que también es necesario crear una nueva PipedInputStream para cada hilo consumidor. Si lees de Pipe de otro hilo, te dará un error. –

+0

@Lawrence: no entiendo su razón de ser para usar 2 hilos ... A MENOS QUE sea un requisito que todos los caracteres leídos de InputStream se escriban en OutputStream de manera oportuna. –

+0

Thx. Pasé por alto PipedStreams al principio porque pensé que sería demasiado engorroso tratar con ellos. Resultó no ser un gran problema, especialmente de Clojure. –

15

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

class1.putDataOnOutputStream (hacia fuera);

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

ofertas Easystream con excepción de propagación y otros problemas desagradables he estado depurando durante aproximadamente un año. (Soy el Mantenedor de la biblioteca: es evidente que mi solución es la mejor;)) Aquí es un ejemplo de cómo se usa:

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

Hay también un buen introduction, donde todas las demás formas de convertir se explica un OutputStream en un InputStream. Vale la pena echar un vistazo.

+1

El tutorial para usar su clase está disponible en https://code.google.com/p/io-tools/wiki/Tutorial_EasyStream – koppor

+0

¡Excelente trabajo! Realmente solucionó mi problema. – smartwjw

4

Creo que la mejor manera de conectar InputStream a un OutputStream es a través de transmisiones por cañería - disponible en java.paquete 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 de este código:

1 - No hay consumo adicional de memoria a excepción de la memoria intermedia.

2 - No es necesario para manejar los datos en cola manualmente

+1

Esto sería genial, pero los [javadocs] (http://docs.oracle.com/javase/7/docs/api/java/io/PipedInputStream.html) dicen que si lees y escribes estos en el mismo Enhebrar, podrías quedarte estancado. ¡Ojalá hubieran actualizado esto con NIO! –

6

Una solución simple que evita la copia de la memoria intermedia es crear un destino especial ByteArrayOutputStream:

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

Comentario a la corriente de salida por encima según sea necesario, llame al toInputStream para obtener un flujo de entrada sobre el búfer subyacente. Considere el flujo de salida como cerrado después de ese punto.

0

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

aquí está mi propuesta de solución: un ProducerInputStream que crea contenido en trozos por las 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); 

} 
Cuestiones relacionadas