2012-02-29 9 views
80

¿Cómo lee la misma línea de entrada dos veces? ¿Es posible copiarlo de alguna manera?Lea la secuencia dos veces

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.

+0

uso pueden ser marca y restablecer –

Respuesta

73

Puede utilizar org.apache.commons.io.IOUtils.copy para copiar el contenido de la InputStream a una matriz de bytes, y después lee repetidamente desde el conjunto de bytes con un ByteArrayInputStream. 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); 
} 
+1

Creo que esta es la única solución válida ya que la marca no es compatible con todos los tipos. – Warpzit

+3

@Paul Grime: IOUtils.toByeArray también internamente llama al método de copia desde adentro. – Ankit

+1

Como dice @Ankit, esta solución no es válida para mí, ya que la entrada se lee internamente y no se puede reutilizar. –

20

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

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

+0

InputStream no soporta 'marca' - se puede llamar marca en un IS pero no hace nada. Del mismo modo, el restablecimiento de llamadas en un IS producirá una excepción. – ayahuasca

4

Si está utilizando una aplicación de InputStream, se puede comprobar el resultado de InputStream#markSupported() que le diga si está o no puede utilizar el método mark()/reset().

Si puede marcar la secuencia cuando lee, llame al reset() para volver al inicio.

Si no puede, tendrá que volver a abrir una secuencia.

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 Convert InputStream to byte array in Java usando libs de terceros o no. Precaución, si el contenido de lectura es demasiado grande, es posible que experimente algunos problemas de memoria.

Por último, si su necesidad es leer la imagen, a continuación, utilizar:

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

Usando ImageIO#read(java.net.URL) también permite el uso de caché.

+1

una palabra de advertencia cuando se usa 'ImageIO # read (java.net.URL)': algunos servidores web y CDN pueden rechazar llamadas vacías (es decir, sin un User Agent que haga creer al servidor que la llamada proviene de un navegador web) hecha por ' ImageIO # read'. En ese caso, usar 'URLConnection.openConnection()' configurando el agente de usuario para esa conexión + usando 'ImageIO.read (InputStream) hará, la mayoría de las veces, el truco. –

+0

'InputStream' no es una interfaz – Brice

+0

@Brice De hecho, ¡gracias por señalar esto! –

2

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

+3

Digo mala idea en este caso, la matriz resultante podría ser enorme y robará el dispositivo de memoria. –

7

si su apoyo InputStream utilizando la marca, entonces se puede mark() su flujoEntrada y luego reset() ella. si su InputStrem no es compatible con la marca a continuación, puede utilizar la clase java.io.BufferedInputStream, por lo que puede integrar su flujo dentro de un BufferedInputStream como esto

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

Usted puede envolver flujo de entrada con PushbackInputStream. PushbackInputStream permite a ("backescritura") bytes leídos que ya se han leído, por lo que se puede hacer así:

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 el buffer interno tiendas PushbackInputStream de bytes por lo que realmente crea un búfer en memoria que contiene bytes "escritos de vuelta".

Conociendo este enfoque podemos ir más allá y combinarlo con FilterInputStream.FilterInputStream almacena la corriente de entrada original como un delegado. Esto permite crear una nueva definición de clase que permite "no leído" datos originales automáticamente. La definición de esta clase es la siguiente:

public class TryReadInputStream extends FilterInputStream { 
    private final int maxPushbackBufferSize; 

    /** 
    * Creates a <code>FilterInputStream</code> 
    * by assigning the argument <code>in</code> 
    * to the field <code>this.in</code> so as 
    * to remember it for later use. 
    * 
    * @param in the underlying input stream, or <code>null</code> 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 <code>length</code> 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 <code>buffer</code> 
    * @aram length how many bytes to read from the stream to buff. Length needs to be less than 
    *  <code>maxPushbackBufferSize</code> 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. Uno para leer en el búfer existente (la definición es análoga a llamar al 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 vamos a ver 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 


    } 



} 
2

¿Qué tal:

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' 

    }