In Kotlin, how do I read the entire contents of an InputStream into a String?

Jayson Minard picture Jayson Minard · Sep 14, 2016 · Viewed 49.3k times · Source

I recently saw code for reading entire contents of an InputStream into a String in Kotlin, such as:

// input is of type InputStream
val baos = ByteArrayOutputStream()
input.use { it.copyTo(baos) }
val inputAsString = baos.toString()

And also:

val reader = BufferedReader(InputStreamReader(input))
try {
    val results = StringBuilder()
    while (true) { 
        val line = reader.readLine()
        if (line == null) break
        results.append(line) 
    }
    val inputAsString = results.toString()
} finally {
    reader.close()
}

And even this that looks smoother since it auto-closes the InputStream:

val inputString = BufferedReader(InputStreamReader(input)).useLines { lines ->
    val results = StringBuilder()
    lines.forEach { results.append(it) }
    results.toString()
}

Or slight variation on that one:

val results = StringBuilder()
BufferedReader(InputStreamReader(input)).forEachLine { results.append(it) }
val resultsAsString = results.toString()   

Then this functional fold thingy:

val inputString = input.bufferedReader().useLines { lines ->
    lines.fold(StringBuilder()) { buff, line -> buff.append(line) }.toString()
}

Or a bad variation which doesn't close the InputStream:

val inputString = BufferedReader(InputStreamReader(input))
        .lineSequence()
        .fold(StringBuilder()) { buff, line -> buff.append(line) }
        .toString()

But they are all clunky and I keep finding newer and different versions of the same... and some of them never even close the InputStream. What is a non-clunky (idiomatic) way to read the InputStream?

Note: this question is intentionally written and answered by the author (Self-Answered Questions), so that the idiomatic answers to commonly asked Kotlin topics are present in SO.

Answer

Jayson Minard picture Jayson Minard · Sep 14, 2016

Kotlin has a specific extensions just for this purpose.

The simplest:

val inputAsString = input.bufferedReader().use { it.readText() }  // defaults to UTF-8

And in this example you could decide between bufferedReader() or just reader(). The call to the function Closeable.use() will automatically close the input at the end of the lambda's execution.

Further reading:

If you do this type of thing a lot, you could write this as an extension function:

fun InputStream.readTextAndClose(charset: Charset = Charsets.UTF_8): String {
    return this.bufferedReader(charset).use { it.readText() }
}

Which you could then call easily as:

val inputAsString = input.readTextAndClose()  // defaults to UTF-8

On a side note, all Kotlin extension functions that require knowing the charset already default to UTF-8, so if you require a different encoding you need to adjust the code above in calls to include an encoding for reader(charset) or bufferedReader(charset).

Warning: You might see examples that are shorter:

val inputAsString = input.reader().readText() 

But these do not close the stream. Make sure you check the API documentation for all of the IO functions you use to be sure which ones close and which do not. Usually if they include the word use (such as useLines() or use()) thy close the stream after. An exception is that File.readText() differs from Reader.readText() in that the former does not leave anything open and the latter does indeed require an explicit close.

See also: Kotlin IO related extension functions