Leer secuencia dos veces

¿Cómo se lee la misma stream de entrada dos veces? ¿Es posible copiarlo de alguna manera?

Necesito obtener una imagen de la web, guardarla localmente y luego devolver la imagen guardada. Solo pensé que sería más rápido usar la misma transmisión en lugar de comenzar una nueva transmisión al contenido descargado y luego volver a leerlo.

Puede usar org.apache.commons.io.IOUtils.copy para copiar el contenido de InputStream a una matriz de bytes, y luego leer repetidamente de la matriz de bytes usando ByteArrayInputStream. P.ej:

 ByteArrayOutputStream baos = new ByteArrayOutputStream(); org.apache.commons.io.IOUtils.copy(in, baos); byte[] bytes = baos.toByteArray(); // either while (needToReadAgain) { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); yourReadMethodHere(bais); } // or ByteArrayInputStream bais = new ByteArrayInputStream(bytes); while (needToReadAgain) { bais.reset(); yourReadMethodHere(bais); } 

Dependiendo de dónde provenga InputStream, es posible que no pueda restablecerlo. Puede verificar si mark() y reset() son compatibles con markSupported() .

Si es así, puede llamar a reset() en InputStream para volver al comienzo. De lo contrario, debe volver a leer InputStream desde la fuente.

Puede envolver la secuencia de entrada con PushbackInputStream. PushbackInputStream permite leer bytes (” escribir de nuevo “) que ya fueron leídos, para que pueda hacer esto:

 public class StreamTest { public static void main(String[] args) throws IOException { byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; InputStream originalStream = new ByteArrayInputStream(bytes); byte[] readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 1 2 3 readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 4 5 6 // now let's wrap it with PushBackInputStream originalStream = new ByteArrayInputStream(bytes); InputStream wrappedStream = new PushbackInputStream(originalStream, 10); // 10 means that maximnum 10 characters can be "written back" to the stream readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 1 2 3 ((PushbackInputStream) wrappedStream).unread(readBytes, 0, readBytes.length); readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 1 2 3 } private static byte[] getBytes(InputStream is, int howManyBytes) throws IOException { System.out.print("Reading stream: "); byte[] buf = new byte[howManyBytes]; int next = 0; for (int i = 0; i < howManyBytes; i++) { next = is.read(); if (next > 0) { buf[i] = (byte) next; } } return buf; } private static void printBytes(byte[] buffer) throws IOException { System.out.print("Reading stream: "); for (int i = 0; i < buffer.length; i++) { System.out.print(buffer[i] + " "); } System.out.println(); } } 

Tenga en cuenta que PushbackInputStream almacena el búfer interno de bytes, por lo que realmente crea un búfer en la memoria que mantiene los bytes "escritos de nuevo".

Conociendo este enfoque, podemos ir más allá y combinarlo con FilterInputStream. FilterInputStream almacena la stream de entrada original como un delegado. Esto permite crear una nueva definición de clase que permite " leer " los datos originales de forma automática. La definición de esta clase es la siguiente:

 public class TryReadInputStream extends FilterInputStream { private final int maxPushbackBufferSize; /** * Creates a FilterInputStream * by assigning the argument in * to the field this.in so as * to remember it for later use. * * @param in the underlying input stream, or null if * this instance is to be created without an underlying stream. */ public TryReadInputStream(InputStream in, int maxPushbackBufferSize) { super(new PushbackInputStream(in, maxPushbackBufferSize)); this.maxPushbackBufferSize = maxPushbackBufferSize; } /** * Reads from input stream the length of bytes to given buffer. The read bytes are still avilable * in the stream * * @param buffer the destination buffer to which read the data * @param offset the start offset in the destination buffer * @aram length how many bytes to read from the stream to buff. Length needs to be less than * maxPushbackBufferSize or IOException will be thrown * * @return number of bytes read * @throws java.io.IOException in case length is */ public int tryRead(byte[] buffer, int offset, int length) throws IOException { validateMaxLength(length); // NOTE: below reading byte by byte instead of "int bytesRead = is.read(firstBytes, 0, maxBytesOfResponseToLog);" // because read() guarantees to read a byte int bytesRead = 0; int nextByte = 0; for (int i = 0; (i < length) && (nextByte >= 0); i++) { nextByte = read(); if (nextByte >= 0) { buffer[offset + bytesRead++] = (byte) nextByte; } } if (bytesRead > 0) { ((PushbackInputStream) in).unread(buffer, offset, bytesRead); } return bytesRead; } public byte[] tryRead(int maxBytesToRead) throws IOException { validateMaxLength(maxBytesToRead); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // as ByteArrayOutputStream to dynamically allocate internal bytes array instead of allocating possibly large buffer (if maxBytesToRead is large) // NOTE: below reading byte by byte instead of "int bytesRead = is.read(firstBytes, 0, maxBytesOfResponseToLog);" // because read() guarantees to read a byte int nextByte = 0; for (int i = 0; (i < maxBytesToRead) && (nextByte >= 0); i++) { nextByte = read(); if (nextByte >= 0) { baos.write((byte) nextByte); } } byte[] buffer = baos.toByteArray(); if (buffer.length > 0) { ((PushbackInputStream) in).unread(buffer, 0, buffer.length); } return buffer; } private void validateMaxLength(int length) throws IOException { if (length > maxPushbackBufferSize) { throw new IOException( "Trying to read more bytes than maxBytesToRead. Max bytes: " + maxPushbackBufferSize + ". Trying to read: " + length); } } } 

Esta clase tiene dos métodos. Una para leer en el búfer existente (la definición es análoga a llamar a public int read(byte b[], int off, int len) de la clase InputStream). Segundo, que devuelve un nuevo buffer (esto puede ser más efectivo si se desconoce el tamaño del buffer para leer).

Ahora veamos nuestra clase en acción:

 public class StreamTest2 { public static void main(String[] args) throws IOException { byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; InputStream originalStream = new ByteArrayInputStream(bytes); byte[] readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 1 2 3 readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 4 5 6 // now let's use our TryReadInputStream originalStream = new ByteArrayInputStream(bytes); InputStream wrappedStream = new TryReadInputStream(originalStream, 10); readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); // NOTE: no manual call to "unread"(!) because TryReadInputStream handles this internally printBytes(readBytes); // prints 1 2 3 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); printBytes(readBytes); // prints 1 2 3 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); printBytes(readBytes); // prints 1 2 3 // we can also call normal read which will actually read the bytes without "writing them back" readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 1 2 3 readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 4 5 6 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); // now we can try read next bytes printBytes(readBytes); // prints 7 8 9 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); printBytes(readBytes); // prints 7 8 9 } } 

si su InputStream admite el uso de mark, entonces puede mark() su inputStream y luego reset() . si su InputStrem no es compatible con la marca, entonces puede usar la clase java.io.BufferedInputStream , por lo que puede incrustar su flujo dentro de un BufferedInputStream como este

  InputStream bufferdInputStream = new BufferedInputStream(yourInputStream); bufferdInputStream.mark(some_value); //read your bufferdInputStream bufferdInputStream.reset(); //read it again 

Si está utilizando una implementación de InputStream , puede verificar el resultado de InputStream#markSupported() que le indica si puede usar el método mark() / reset() .

Si puede marcar la secuencia cuando lee, luego llame a reset() para volver a comenzar.

Si no puede, tendrá que abrir una transmisión nuevamente.

Otra solución sería convertir InputStream en matriz de bytes, luego iterar sobre la matriz tantas veces como lo necesite. Puede encontrar varias soluciones en esta publicación Convertir InputStream en matriz de bytes en Java utilizando librerías de terceros o no. Precaución, si el contenido de lectura es demasiado grande, es posible que experimente algunos problemas de memoria.

Finalmente, si su necesidad es leer una imagen, entonces use:

 BufferedImage image = ImageIO.read(new URL("http://www.example.com/images/toto.jpg")); 

El uso de ImageIO#read(java.net.URL) también le permite usar caché.

Convierte inputstream en bytes y luego lo pasa a la función savefile donde lo ensamblas en inputstream. También en la función original use bytes para usar en otras tareas

Qué tal si:

 if (stream.markSupported() == false) { // lets replace the stream object ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOUtils.copy(stream, baos); stream.close(); stream = new ByteArrayInputStream(baos.toByteArray()); // now the stream should support 'mark' and 'reset' }