¿Cómo convierto un OutputStream en un InputStream?
Estoy en la etapa de desarrollo donde tengo dos módulos y de uno obtuve el resultado como OutputStream
, y el segundo que acepta solo InputStream
.
¿Sabes cómo convertir OutputStream
a InputStream
(no al revés, quiero decir realmente de esta manera) para poder conectar estas dos partes?
Parece haber muchos enlaces y otras cosas similares, pero ningún código real que utilice tuberías. La ventaja de utilizar java.io.PipedInputStream
y java.io.PipedOutputStream
es que no hay consumo adicional de memoria. ByteArrayOutputStream.toByteArray()
devuelve una copia del búfer original, lo que significa que lo que sea que tengas en la memoria, ahora tienes dos copias. Luego, escribir en un InputStream
significa que ahora tiene tres copias de los datos.
El código que usa lambdas
(sombrero para @John Manko de los comentarios):
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
// in a background thread, write the given output stream to the
// PipedOutputStream for consumption
new Thread(() -> {originalOutputStream.writeTo(out);}).start();
Una cosa que @John Manko notó es que en ciertos casos, cuando no tienes control de la creación de OutputStream , puedes terminar en una situación en la que el creador puede limpiar el objeto OutputStream prematuramente. Si obtienes el ClosedPipeException
, deberías intentar invertir los constructores:
PipedInputStream in = new PipedInputStream(out);
new Thread(() -> {originalOutputStream.writeTo(out);}).start();
Tenga en cuenta que también puede invertir los constructores de los ejemplos siguientes.
Gracias también a @AlexK por corregirme al iniciar un Thread
en lugar de simplemente iniciar un Runnable
.
El código usando try-with-resources
:
// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
new Thread(new Runnable() {
public void run () {
// try-with-resources here
// putting the try block outside the Thread will cause the
// PipedOutputStream resource to close before the Runnable finishes
try (final PipedOutputStream out = new PipedOutputStream(in)) {
// write the original OutputStream to the PipedOutputStream
// note that in order for the below method to work, you need
// to ensure that the data has finished writing to the
// ByteArrayOutputStream
originalByteArrayOutputStream.writeTo(out);
}
catch (IOException e) {
// logging and exception handling should go here
}
}
}).start();
El código original que escribí:
// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
new Thread(new Runnable() {
public void run () {
try {
// write the original OutputStream to the PipedOutputStream
// note that in order for the below method to work, you need
// to ensure that the data has finished writing to the
// ByteArrayOutputStream
originalByteArrayOutputStream.writeTo(out);
}
catch (IOException e) {
// logging and exception handling should go here
}
finally {
// close the PipedOutputStream here because we're done writing data
// once this thread has completed its run
if (out != null) {
// close the PipedOutputStream cleanly
out.close();
}
}
}
}).start();
Este código supone que originalByteArrayOutputStream
es un, ByteArrayOutputStream
ya que suele ser el único flujo de salida utilizable, a menos que esté escribiendo en un archivo. Lo mejor de esto es que, dado que está en un hilo separado, también funciona en paralelo, por lo que cualquier cosa que consuma su flujo de entrada también saldrá de su antiguo flujo de salida. Esto es beneficioso porque el búfer puede permanecer más pequeño y tendrá menos latencia y menos uso de memoria.
Si no tiene un ByteArrayOutputStream
, en lugar de usarlo writeTo()
, tendrá que usar uno de los write()
métodos de la java.io.OutputStream
clase o uno de los otros métodos disponibles en una subclase.
An OutputStream
es aquel en el que escribes datos. Si algún módulo expone un OutputStream
, la expectativa es que haya algo leyendo en el otro extremo.
Algo que expone un InputStream
, por otro lado, indica que necesitarás escuchar esta transmisión y habrá datos que podrás leer.
Entonces es posible conectar un InputStream
a unOutputStream
InputStream----read---> intermediateBytes[n] ----write----> OutputStream
Como alguien mencionó, esto es lo que le permite hacer el copy()
método de IOUtils . No tiene sentido ir al otro lado... ojalá esto tenga algún sentido.
ACTUALIZAR:
Por supuesto, cuanto más pienso en esto, más puedo ver cómo en realidad sería un requisito. Sé que algunos de los comentarios mencionaron Piped
flujos de entrada/salida, pero existe otra posibilidad.
Si el flujo de salida expuesto es ByteArrayOutputStream
, siempre puede obtener el contenido completo llamando al toByteArray()
método. Luego puede crear un contenedor de flujo de entrada utilizando la ByteArrayInputStream
subclase. Estos dos son pseudo-flujos, básicamente ambos simplemente envuelven una matriz de bytes. Por lo tanto, utilizar los streams de esta manera es técnicamente posible, pero para mí sigue siendo muy extraño...
Como los flujos de entrada y salida son solo puntos de inicio y finalización, la solución es almacenar datos temporalmente en una matriz de bytes. Por lo tanto, debe crear un intermedio ByteArrayOutputStream
, a partir del cual crea byte[]
y que se utiliza como entrada para el nuevo ByteArrayInputStream
.
public void doTwoThingsWithStream(InputStream inStream, OutputStream outStream){
//create temporary bayte array output stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doFirstThing(inStream, baos);
//create input stream from baos
InputStream isFromFirstData = new ByteArrayInputStream(baos.toByteArray());
doSecondThing(isFromFirstData, outStream);
}
Espero eso ayude.
ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream;
byte[] bytes = buffer.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);