¿Cómo se lee el mismo flujo 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. Simplemente pensé que sería más rápido usar la misma secuencia en lugar de comenzar una nueva secuencia para el contenido descargado y luego volver a leerla.
java
inputstream
Warpzit
fuente
fuente
Respuestas:
Puede usar
org.apache.commons.io.IOUtils.copy
para copiar el contenido de InputStream en una matriz de bytes, y luego leer repetidamente desde la matriz de bytes utilizando un ByteArrayInputStream. P.ej:fuente
Dependiendo de dónde provenga InputStream, es posible que no pueda restablecerlo. Puede verificar si
mark()
yreset()
son compatibles conmarkSupported()
.Si es así, puede llamar
reset()
al InputStream para volver al principio. De lo contrario, debe leer InputStream desde la fuente nuevamente.fuente
InputStream
subclases comoBufferedInputStream
admite 'marca'Si es
InputStream
compatible con Mark, entonces puedemark()
su inputStream y luegoreset()
. si suInputStrem
marca no es compatible, entonces puede usar la clasejava.io.BufferedInputStream
, por lo que puede incrustar su transmisión dentro de un estiloBufferedInputStream
como estefuente
BufferedInputStream.fill()
, existe la sección "crecer buffer", donde el nuevo tamaño de buffer se compara solo conmarklimit
yMAX_BUFFER_SIZE
.Puede ajustar la secuencia de entrada con PushbackInputStream. PushbackInputStream permite no leer (" escribir ") bytes que ya se leyeron, por lo que puede hacer esto:
Tenga en cuenta que PushbackInputStream almacena el búfer interno de bytes, por lo que realmente crea un búfer en la memoria que contiene los bytes "reescritos".
Conociendo este enfoque podemos ir más allá y combinarlo con FilterInputStream. FilterInputStream almacena la secuencia de entrada original como delegado. Esto permite crear una nueva definición de clase que permite " no leer " los datos originales automáticamente. La definición de esta clase es la siguiente:
Esta clase tiene dos métodos. Uno para leer en el búfer existente (la definición es análoga a la llamada
public int read(byte b[], int off, int len)
de la clase InputStream). Segundo, que devuelve un nuevo búfer (esto puede ser más efectivo si se desconoce el tamaño del búfer para leer).Ahora veamos a nuestra clase en acción:
fuente
Si está utilizando una implementación de
InputStream
, puede verificar el resultadoInputStream#markSupported()
que le indica si puede usar el métodomark()
/reset()
.Si puede marcar la transmisión cuando lee, entonces llame
reset()
para regresar para comenzar.Si no puede, tendrá que abrir una transmisión nuevamente.
Otra solución sería convertir InputStream en una matriz de bytes, luego iterar sobre la matriz tantas veces como sea necesario. Puede encontrar varias soluciones en esta publicación Convertir InputStream a matriz de bytes en Java utilizando bibliotecas de terceros o no. Precaución, si el contenido leído es demasiado grande, puede experimentar algunos problemas de memoria.
Finalmente, si su necesidad es leer la imagen, use:
Usar
ImageIO#read(java.net.URL)
también le permite usar caché.fuente
ImageIO#read(java.net.URL)
: algunos servidores web y CDN pueden rechazar llamadas desnudas (es decir, sin un Agente de usuario que haga creer al servidor que la llamada proviene de un navegador web) realizada porImageIO#read
. En ese caso, usar laURLConnection.openConnection()
configuración del agente de usuario para esa conexión + usar `ImageIO.read (InputStream), la mayoría de las veces, hará el truco.InputStream
no es una interfazQué tal si:
fuente
Para dividir un
InputStream
en dos, evitando cargar todos los datos en la memoria y luego procesarlos de forma independiente:OutputStream
, precisamente:PipedOutputStream
PipedInputStream
son los devueltosInputStream
.OutputStream
. Entonces, todo lo que se lee del abastecimientoInputStream
, se escribiría en ambosOutputStream
. No es necesario implementar eso, porque ya está hecho enTeeInputStream
(commons.io).Dentro de un hilo separado, lea toda la fuente inputStream, e implícitamente los datos de entrada se transfieren a los inputStreams de destino.
Tenga cuidado de cerrar inputStreams después de ser consumido y cierre el hilo que se ejecuta:
TeeInputStream.readAllBytes()
En caso de que necesite dividirlo en múltiples
InputStream
, en lugar de solo dos. Reemplace en el fragmento de código anterior la claseTeeOutputStream
para su propia implementación, que encapsularíaList<OutputStream>
ay anularía laOutputStream
interfaz:fuente
Convierta inputstream en bytes y luego páselo a la función savefile donde ensambla el mismo en inputstream. También en la función original, use bytes para otras tareas
fuente
En caso de que alguien esté ejecutando una aplicación Spring Boot y desee leer el cuerpo de respuesta de un
RestTemplate
(por eso quiero leer una secuencia dos veces), hay una manera limpia (er) de hacerlo.En primer lugar, debe usar Spring's
StreamUtils
para copiar la secuencia a una Cadena:Pero eso no es todo. También debe usar una fábrica de solicitudes que pueda almacenar en búfer la transmisión por usted, de esta manera:
O, si está utilizando el bean de fábrica, entonces (esto es Kotlin pero de todos modos):
Fuente: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
fuente
Si está utilizando RestTemplate para hacer llamadas http Simplemente agregue un interceptor. El cuerpo de respuesta se almacena en caché por la implementación de ClientHttpResponse. Ahora inputstream se puede recuperar de respose tantas veces como sea necesario
fuente