Tengo un InputStream que paso a un método para hacer algún procesamiento. Usaré el mismo InputStream en otro método, pero después del primer procesamiento, el InputStream parece estar cerrado dentro del método.
¿Cómo puedo clonar el InputStream para enviarlo al método que lo cierra? ¿Hay otra solución?
EDITAR: los métodos que cierran InputStream es un método externo de una lib. No tengo control sobre el cierre o no.
private String getContent(HttpURLConnection con) {
InputStream content = null;
String charset = "";
try {
content = con.getInputStream();
CloseShieldInputStream csContent = new CloseShieldInputStream(content);
charset = getCharset(csContent);
return IOUtils.toString(content,charset);
} catch (Exception e) {
System.out.println("Error downloading page: " + e);
return null;
}
}
private String getCharset(InputStream content) {
try {
Source parser = new Source(content);
return parser.getEncoding();
} catch (Exception e) {
System.out.println("Error determining charset: " + e);
return "UTF-8";
}
}
java
clone
inputstream
Renato Dinhani
fuente
fuente
Respuestas:
Si todo lo que quiere hacer es leer la misma información más de una vez, y los datos de entrada son lo suficientemente pequeños como para caber en la memoria, puede copiar los datos desde su
InputStream
a un ByteArrayOutputStream .Luego puede obtener la matriz de bytes asociada y abrir tantos ByteArrayInputStream "clonados" como desee.
Pero si realmente necesita mantener abierta la transmisión original para recibir nuevos datos, deberá realizar un seguimiento de este
close()
método externo y evitar que se llame de alguna manera.ACTUALIZACIÓN (2019):
Desde Java 9, los bits medios se pueden reemplazar con
InputStream.transferTo
:fuente
TeeInputStream
como se describe en la respuesta aquí .Quieres usar Apache's
CloseShieldInputStream
:Este es un contenedor que evitará que la secuencia se cierre. Harías algo como esto.
fuente
CloseShield
no funciona porque suHttpURLConnection
flujo de entrada original está cerrado en alguna parte. ¿No debería su método llamar a IOUtils con la secuencia protegidaIOUtils.toString(csContent,charset)
?close()
llamada en absoluto, sino el hecho de que Stream se está leyendo hasta el final. Dadomark()
yreset()
no son necesariamente los mejores métodos para conexiones HTTP, tal vez debería echar un vistazo a la matriz de bytes enfoque descrito en mi respuesta.No puede clonarlo, y cómo va a resolver su problema depende de cuál sea la fuente de los datos.
Una solución es leer todos los datos del InputStream en una matriz de bytes, y luego crear un ByteArrayInputStream alrededor de esa matriz de bytes, y pasar esa secuencia de entrada a su método.
Edición 1: es decir, si el otro método también necesita leer los mismos datos. Es decir, desea "restablecer" la transmisión.
fuente
Si los datos leídos de la transmisión son grandes, recomendaría usar un TeeInputStream de Apache Commons IO. De esa manera, esencialmente puede replicar la entrada y pasar una tubería t'd como su clon.
fuente
Esto podría no funcionar en todas las situaciones, pero esto es lo que hice: extendí la clase FilterInputStream y realicé el procesamiento requerido de los bytes a medida que la biblioteca externa lee los datos.
Luego, simplemente pasa una instancia de
StreamBytesWithExtraProcessingInputStream
dónde habría pasado en la secuencia de entrada. Con la secuencia de entrada original como parámetro constructor.Cabe señalar que esto funciona byte por byte, así que no lo use si se requiere un alto rendimiento.
fuente
UPD Mira el comentario antes. No es exactamente lo que se le preguntó.
Si está utilizando
apache.commons
, puede copiar transmisiones utilizandoIOUtils
.Puedes usar el siguiente código:
Aquí está el ejemplo completo adecuado para su situación:
Este código requiere algunas dependencias:
MAVEN
GRADLE
Aquí está la referencia DOC para este método:
Puede encontrar más información
IOUtils
aquí: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)fuente
A continuación se muestra la solución con Kotlin.
Puede copiar su InputStream en ByteArray
Si necesita leer
byteInputStream
varias veces, llamebyteInputStream.reset()
antes de volver a leer.https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
fuente
La siguiente clase debería hacer el truco. Simplemente cree una instancia, llame al método de "multiplicación" y proporcione el flujo de entrada de origen y la cantidad de duplicados que necesita.
Importante: debe consumir todas las secuencias clonadas simultáneamente en subprocesos separados.
fuente
La clonación de una secuencia de entrada podría no ser una buena idea, ya que esto requiere un conocimiento profundo sobre los detalles de la secuencia de entrada que se está clonando. Una solución para esto es crear una nueva secuencia de entrada que lea de la misma fuente nuevamente.
Entonces, usando algunas características de Java 8, esto se vería así:
Este método tiene el efecto positivo de que reutilizará el código que ya está en su lugar: la creación de la secuencia de entrada encapsulada en
inputStreamSupplier
. Y no es necesario mantener una segunda ruta de código para la clonación de la secuencia.Por otro lado, si la lectura de la transmisión es costosa (porque se realiza a través de una conexión de bajo ancho de banda), entonces este método duplicará los costos. Esto podría evitarse utilizando un proveedor específico que almacenará el contenido de la secuencia localmente primero y proporcionará un
InputStream
recurso local para ese momento.fuente
is
?java.io.File
o,java.net.URL
por ejemplo, para crear una nueva secuencia de entrada cada vez que se invoca.