我有一个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
会引入很多开销。还有更好的方法吗?
答案 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}}返回一个空流。
由于你已经有了性能基准,我会留给你判断是否效率更高。