Scala: InputStream to Array[Byte]
With Scala, what is the best way to read from an InputStream to a bytear开发者_高级运维ray?
I can see that you can convert an InputStream to char array
Source.fromInputStream(is).toArray()
How about:
Stream.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray
Update: use LazyList instead of Stream
(since Stream
is deprecated in Scala 3)
LazyList.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray
Just removed bottleneck in our server code by replacing
Stream.continually(request.getInputStream.read()).takeWhile(_ != -1).map(_.toByte).toArray
with
org.apache.commons.io.IOUtils.toByteArray(request.getInputStream)
Or in pure Scala:
def bytes(in: InputStream, initSize: Int = 8192): Array[Byte] = {
var buf = new Array[Byte](initSize)
val step = initSize
var pos, n = 0
while ({
if (pos + step > buf.length) buf = util.Arrays.copyOf(buf, buf.length << 1)
n = in.read(buf, pos, step)
n != -1
}) pos += n
if (pos != buf.length) buf = util.Arrays.copyOf(buf, pos)
buf
}
Do not forget to close an opened input stream in any case:
val in = request.getInputStream
try bytes(in) finally in.close()
In a similar vein to Eastsun's answer... I started this as a comment, but it ended up getting just a bit to long!
I'd caution against using Stream
, if holding a reference to the head element then streams can easily consume a lot of memory.
Given that you're only going to read in the file once, then Iterator
is a much better choice:
def inputStreamToByteArray(is: InputStream): Array[Byte] =
Iterator continually is.read takeWhile (-1 !=) map (_.toByte) toArray
import scala.tools.nsc.io.Streamable
Streamable.bytes(is)
Don't remember how recent that is: probably measured in days. Going back to 2.8, it's more like
new Streamable.Bytes { def inputStream() = is } toByteArray
With Scala IO, this should work:
def inputStreamToByteArray(is: InputStream): Array[Byte] =
Resource.fromInputStream(in).byteArray
With better-files, you can simply do is.bytes
Source.fromInputStream(is).map(_.toByte).toArray
How about buffered version of solution based on streams plus ByteArraOutputStream to minimize boilerplate around final array growing?
val EOF: Int = -1
def readBytes(is: InputStream, bufferSize: Int): Array[Byte] = {
val buf = Array.ofDim[Byte](bufferSize)
val out = new ByteArrayOutputStream(bufferSize)
Stream.continually(is.read(buf)) takeWhile { _ != EOF } foreach { n =>
out.write(buf, 0, n)
}
out.toByteArray
}
Here's an approach using scalaz-stream:
import scalaz.concurrent.Task
import scalaz.stream._
import scodec.bits.ByteVector
def allBytesR(is: InputStream): Process[Task, ByteVector] =
io.chunkR(is).evalMap(_(4096)).reduce(_ ++ _).lastOr(ByteVector.empty)
Since JDK 9:
is.readAllBytes()
We can do using Google API ByteStreams
com.google.common.io.ByteStreams
pass the stream to ByteStreams.toByteArray method for conversion
ByteStreams.toByteArray(stream)
def inputStreamToByteArray(is: InputStream): Array[Byte] = {
val buf = ListBuffer[Byte]()
var b = is.read()
while (b != -1) {
buf.append(b.byteValue)
b = is.read()
}
buf.toArray
}
精彩评论