如何将Scala流的内容写入文件?

时间:2015-04-30 21:40:18

标签: scala io

我有一个Scala字节流,我想写入文件。该流有太多数据来缓冲所有内存。

作为第一次尝试,我创建了一个类似于此的InputStream

class MyInputStream(data: Stream[Byte]) extends InputStream {
  private val iterator = data.iterator
  override def read(): Int = if (iterator.hasNext) iterator.next else -1
}

然后我使用Apache Commons编写文件:

val source = new MyInputStream(dataStream)
val target = new FileOutputStream(file)
try {
  IOUtils.copy(source, target)
} finally {
  target.close
}

这有效,但我对表现不太满意。我猜测为每个字节调用MyInputStream.read会引入很多开销。还有更好的方法吗?

3 个答案:

答案 0 :(得分:5)

您可能(或可能不会)错误地认为阅读方是您的表现问题的根源。可能是您正在使用无缓冲的FileOutputStream(...),对每个写入的字节强制执行单独的系统调用。

这是我的看法,快速简单:

def writeBytes( data : Stream[Byte], file : File ) = {
  val target = new BufferedOutputStream( new FileOutputStream(file) );
  try data.foreach( target.write(_) ) finally target.close;
}

答案 1 :(得分:2)

您应该在InputStream实现中实现批量读取覆盖:

override def read(b: Array[Byte], off: Int, len: Int)

IOUtils.copy使用该签名以4K块的形式进行读/写。

答案 2 :(得分:2)

鉴于StreamIterator一次读取一个字节可能是瓶颈,我设计了一种方法将流写入OutputStream,而不依赖它,希望更高效:

object StreamCopier {
  def copy(data: Stream[Byte], output: OutputStream) = {
    def write(d: Stream[Byte]): Unit = if (d.nonEmpty) {
      val (head, tail) = d.splitAt(4 * 1024)
      val bytes = head.toArray
      output.write(bytes, 0, bytes.length)
      write(tail)
    }
    write(data)
  }
}

编辑:通过在尾递归data函数中将d替换为write来修复错误。

这种方法通过splitAt使用递归方法将流分割为第一个~4K和其余部分,将其写入OutputStream并递归到流的尾部,直到{ {1}}返回一个空流。

由于你已经有了性能基准,我会留给你判断是否效率更高。