【问题标题】:Scala: InputStream to Array[Byte]Scala:输入流到数组 [字节]
【发布时间】:2011-06-21 18:45:12
【问题描述】:

对于 Scala,从 InputStream 读取到字节数组的最佳方法是什么?

我可以看到您可以将 InputStream 转换为 char 数组

Source.fromInputStream(is).toArray()

【问题讨论】:

    标签: scala bytearray inputstream


    【解决方案1】:

    怎么样:

    Stream.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray
    

    更新:使用 LazyList 代替 Stream(因为 Stream 在 Scala 3 中已弃用)

    LazyList.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray
    

    【讨论】:

    • 你能解释一下这个和问题中的变体之间的区别吗?
    • @Jus12 我正在寻找一个字节数组。我的问题是一种获取 char 数组的方法。
    • 不会创建一个巨大的链表,然后将其转换为数组吗?在时间或内存方面,这看起来效率不高。
    • 看起来这毕竟没有创建一个链表。 Stream.continually 产生一个迭代器,takeWhilemap 似乎将迭代器转换为迭代器。例如。在 Scala 2.9.3 REPL 中评估 Array(1, 2, 3, 4, -1).iterator.takeWhile(-1 !=).map(_.toByte) 给我 Iterator[Byte] = non-empty iterator
    • 这似乎对我造成了 OOM 错误。事情最终被 GC 处理了,但峰值超出了我的服务器可以处理的范围。
    【解决方案2】:

    刚刚通过替换消除了我们服务器代码中的瓶颈

    Stream.continually(request.getInputStream.read()).takeWhile(_ != -1).map(_.toByte).toArray
    

    org.apache.commons.io.IOUtils.toByteArray(request.getInputStream)
    

    或者在纯 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
    }
    

    在任何情况下都不要忘记关闭打开的输入流:

    val in = request.getInputStream
    try bytes(in) finally in.close()
    

    【讨论】:

    • 那是 org.apache.commons.io.IOUtils.toByteArray,以防有人想知道。
    • 这肯定感觉更快。有人对较大的文件进行过任何基准测试或测试吗?
    • 谢谢。使用 Apache Spark 运行它时,我遇到了 GC Overhead 错误的巨大问题,我的任务有 90% 的时间都花在了 GC 上。替换为toByteArray 大大加快了速度。
    • 重要的是要指出这个解决方案如何真正大大超越替代方案,你有像 map(_.toByte) 这样的东西逐字节迭代输入......你正在做这个吗大数据!
    【解决方案3】:

    与 Eastsun 的回答类似...我一开始是评论,但最终变得有点长!

    我建议不要使用Stream,如果持有对头元素的引用,那么流很容易消耗大量内存。

    鉴于您只会在文件中读取一次,那么Iterator 是一个更好的选择:

    def inputStreamToByteArray(is: InputStream): Array[Byte] =
      Iterator continually is.read takeWhile (-1 !=) map (_.toByte) toArray
    

    【讨论】:

      【解决方案4】:
      import scala.tools.nsc.io.Streamable
      Streamable.bytes(is)
      

      不记得最近的时间:可能以天为单位。回到2.8,更像

      new Streamable.Bytes { def inputStream() = is } toByteArray
      

      【讨论】:

      • 使用 scala.tools 包中的东西安全吗?它们甚至是标准库的一部分吗?
      • 没有。但是,如果您想知道如何编写它,就在这里。
      • 它现在似乎已经转移到更标准的scala.reflect.io 包。
      • scala.reflect.io.Streamable.bytes
      【解决方案5】:

      使用Scala IO,这应该可以:

      def inputStreamToByteArray(is: InputStream): Array[Byte] = 
         Resource.fromInputStream(in).byteArray
      

      【讨论】:

        【解决方案6】:

        使用better-files,您可以简单地使用is.bytes

        【讨论】:

        • better.files 应该只在标准库中。它好多了。此外,如果您想要Array[Byte],则需要使用is.byteArray
        【解决方案7】:

        Source.fromInputStream(is).map(_.toByte).toArray

        【讨论】:

        【解决方案8】:

        基于流和 ByteArraOutputStream 的缓冲版本解决方案如何最大限度地减少最终数组增长的样板?

        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
        }
        

        【讨论】:

          【解决方案9】:

          这是一种使用 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)
          

          【讨论】:

          • 可能没有理由减少,这会破坏流的增量特性
          • 原因是题目要求的是字节数组。
          【解决方案10】:

          我们可以使用 Google API ByteStreams

          com.google.common.io.ByteStreams
          

          将流传递给 ByteStreams.toByteArray 方法进行转换

          ByteStreams.toByteArray(stream)
          

          【讨论】:

            【解决方案11】:
            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
            }
            

            【讨论】:

            • List[Byte] 有方法“add”吗?
            猜你喜欢
            • 2011-05-15
            • 2023-03-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-26
            相关资源
            最近更新 更多