Convierta InputStream en una cadena en Java

En este tutorial, veremos cómo convertir un InputStream en una cadena de Java, en una gran cantidad de formas con ejemplos prácticos.

La clase InputStream es una clase de alto nivel que representa cualquier flujo de bytes de entrada en Java. Varias subclases especifican aún más su uso, como BufferedInputStream, ByteArrayInputStream, SequenceInputStream, etc.

No hace falta decir que un InputStream puede almacenar cualquier dato y, en algunos casos, almacena contenido de cadenas como un flujo de bytes.

En este tutorial, veremos cómo convertir un InputStream en una cadena Java.

Comencemos creando un InputStream que usaremos a lo largo de los ejemplos. Estos pueden provenir de varias fuentes, pero hagamos uno a partir de una Cadena, para que sea más fácil verificar si la salida es correcta:

1
2
String string = "Input data, to be converted into an InputStream.";
InputStream inputStream = new ByteArrayInputStream(string.getBytes());

InputStream es una clase abstracta, y hemos usado una de sus subclases, ByteArrayInputStream para leer los bytes de String. Ahora, tenemos un InputStream que juega el papel de entrada real, que podemos verificar fácilmente comparándolo con la instancia de string.

Java tiene una plétora de clases para trabajar con flujos de bytes, y no hay escasez de opciones y enfoques aquí. Nos centraremos en algunos de ellos:

InputStream a cadena con InputStream.readAllBytes()

Desde Java 9, este proceso se ha simplificado mucho. El InputStream se instancia muy comúnmente como un ByteArrayInputStream, antes de llamar a toByteArray() para recuperar los bytes de él.

Este proceso se agiliza y reemplaza por el método integrado InputStream.readAllBytes(), que simplemente devuelve los bytes de InputStream, un método de utilidad muy necesario.

La clase String acepta una matriz byte en su constructor, a partir de la cual se forma y devuelve una String, lo que hace que este sea el enfoque más fácil y más legible para convertir un InputStream en un Cadena:

1
2
String result = new String(inputStream.readAllBytes());
System.out.println(result);

Esto resulta en:

1
Input data, to be converted into an InputStream.

Nota: Si InputStream contiene más de Integer.MAX_VALUE bytes, el método genera naturalmente un OutOfMemoryError.

InputStream a cadena con ByteArrayOutputStream

Para las versiones anteriores a Java 9, la forma más rápida de convertir un InputStream en un String es usando ByteArrayOutputStream.

Este enfoque se basa en el hecho de que podemos construir cadenas fácilmente a partir de matrices de bytes y en el hecho de que ByteArrayOutputStream tiene un método toString() muy útil.

Este proceso es prácticamente el reverso de lo que hicimos al principio: construir un InputStream a partir de una cadena:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Instantiate an OutputStream that'll store the data from the InputStream
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

// For each line of the InputStream, write it to the OutputStream, and move to the next
for (int data = inputStream.read(); data != -1; data = inputStream.read()) {
    byteArrayOutputStream.write(data);
}

// Convert byte array into String
// Optional: You can set a character set via `StandardCharsets` here
String result = byteArrayOutputStream.toString(StandardCharsets.UTF_8);
System.out.println(result);

El método inputStream.read() lee el siguiente byte de la matriz, comenzando por el primero. Aquí, guardamos una referencia al primer byte como datos de esa operación de lectura, comprobamos si es -1 (llegó al final de la transmisión) y pasamos al siguiente byte con otro read() operación.

Ejecutar este código da como resultado:

1
Input data, to be converted into an InputStream.

InputStream to String con InputStreamReader

Las clases InputStream y OutputStream son parte del paquete java.io, que también incluye una clase InputStreamReader realmente útil, originalmente pensada como la clase a usar para leer InputStreams.

InputStreamReader lee los bytes de la secuencia y los decodifica en caracteres, para lo cual también puede especificar opcionalmente a través de una enumeración StandardCharsets:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Read the InputStream into a Reader
Reader reader = new InputStreamReader(inputStream);

// Instantiate a StringBuilder to save the result
StringBuilder result = new StringBuilder();

// Read each byte and convert into a char, adding to the StringBuilder
for (int data = reader.read(); data != -1; data = reader.read()) {
    result.append((char)data);
}

// Convert StringBuilder to String
System.out.println(result.toString());

Esto también resulta en:

1
Input data, to be converted into an InputStream.

InputStream to String con BufferedReader

En lugar de un Reader, también puede usar un BufferedReader. Un BufferedReader almacena grandes cantidades de bytes en un búfer antes de realizar cualquier operación de lectura/escritura. Debido al menor cambio de contexto, esta es una forma más eficiente de leer y escribir grandes cantidades de datos, mientras que en cantidades más pequeñas, la diferencia es imperceptible.

En el ejemplo anterior, podríamos haber envuelto InputStreamReader en un BufferedReader en su lugar:

1
2
3
4
5
6
7
8
9
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

StringBuilder sb = new StringBuilder();

for (int data = reader.read(); data != -1; data = reader.read()) {
    sb.append((char)data);
}

System.out.println(sb.toString());

Esto da como resultado la misma salida:

1
Input data, to be converted into an InputStream.

Nota: Este enfoque es preferible al anterior debido a la mayor eficiencia, aunque puede pasar desapercibido en cantidades de datos más pequeñas.

InputStream to String with BufferedReader.lines()

Con Java 8, la clase BufferedReader obtuvo un nuevo método lines(), que devuelve un Stream de Strings, para cada línea. Esto hace que sea sin esfuerzo leer un ‘InputStream’, usando la API Stream:

1
2
3
4
5
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

String result = reader.lines().collect(Collectors.joining(System.lineSeparator()));

System.out.println(result);

El método Collectors.joining() puede funcionar sin que especifiquemos un delimitador, aunque puede generar resultados inesperados cuando los datos de entrada tienen nuevas líneas. Al establecer el delimitador en System.lineSeparator(), estamos permitiendo que el mecanismo del sistema subyacente se active para los finales de las líneas.

Este código da como resultado:

1
Input data, to be converted into an InputStream.

InputStream a cadena con Apache Commons

Finalmente, echemos un vistazo a cómo lograr esto a través de una biblioteca externa: Apache Commons, que está presente en una cantidad significativa de proyectos.

Para usar Apache Commons, tendremos que agregar su dependencia a nuestro propio proyecto:

1
2
3
4
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
</dependency>

Y con eso fuera del camino, podemos utilizar su clase IOUtils:

1
2
String result = IOUtils.toString(inputStream);
System.out.println(result);

El método también acepta una enumeración StandardCharsets opcional:

1
String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);

Ejecutar este código da como resultado:

1
Input data, to be converted into an InputStream.

Conclusión

En este tutorial, hemos echado un vistazo a cómo convertir un InputStream en una cadena en Java.

Hemos echado un vistazo al enfoque más nuevo, leyendo todos los bytes y construyendo una cadena directamente, así como escribiéndolos en un ‘ByteArrayOutputStream’, antes de usar un ‘Lector’ y ‘BufferedReader’ y finalmente completarlo con el uso de Apache Commons' IOUtils. `.