我正在尝试使用akka流传输文件,并且遇到一个小问题,将流的结果提取到Future [String]中:
def streamMigrationFile(source: Source[ByteString, _]): Future[String] = {
var fileString = ""
val sink = Sink.foreach[ByteString](byteString => fileString =
fileString.concat(byteString.decodeString("US-ASCII")))
source.runWith(sink)
}
我收到了编译错误:
Expression of type Future[Done] does not conform to expected type Future[String]
任何人都可以帮助我理解我做错了什么以及我需要做些什么来提取流的结果?
答案 0 :(得分:3)
如果我猜测的是正确的,您希望将整个文件内容流式传输到字符串中。最好使用Sink.fold
,而不是Sink.foreach
。示例如下。
def streamMigrationFile(source: Source[ByteString, _]): Future[String] = {
val sink = Sink.fold[String, ByteString]("") { case (acc, str) =>
acc + str.decodeString("US-ASCII")
}
source.runWith(sink)
}
您可能已经意识到这一点,但您的文件需要适合内存才能使程序正常运行。
答案 1 :(得分:2)
如果你看一下Sink.foreach
的定义,你会发现评估类型是Sink[T, Future[Done]]
,这意味着计算结果会发生什么并不重要。流中的元素。以下是定义:
def foreach[T](f: T ⇒ Unit): Sink[T, Future[Done]]
另一方面,Sink.fold
的定义评估为Future[U]
U
zero
的类型Sink.fold
。换句话说,您可以在处理结束时定义未来的类型。
以下是def fold[U, T](zero: U)(f: (U, T) ⇒ U): Sink[T, Future[U]] =
Flow[T].fold(zero)(f).toMat(Sink.head)(Keep.right).named("foldSink")
的定义(和实现):
Future[U]
根据上面的实现,您可以看到实现中要保留的类型为Keep.right
,因为T
意味着:"我不在乎如果进入的元素是ByteString
s(或者你的情况为U
),我(流)将为您提供String
s(或Future
)。当我完成时(在Sink.foreach
中)"
以下是您使用Sink.fold
替换Future[String]
并将整个表达式评估为def streamMigrationFile(source: Source[ByteString, _]): Future[String] = {
var fileString = ""
//def foreach[T](f: T ⇒ Unit): Sink[T, Future[Done]]
val sinkForEach: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString](byteString => fileString =
fileString.concat(byteString.decodeString("US-ASCII")))
/*
def fold[U, T](zero: U)(f: (U, T) ⇒ U): Sink[T, Future[U]] =
Flow[T].fold(zero)(f).toMat(Sink.head)(Keep.right).named("foldSink")
*/
val sinkFold: Sink[ByteString, Future[String]] = Sink.fold("") { case (acc, str) =>
acc + str
}
val res1: Future[Done] = source.runWith(sinkForEach)
val res2: Future[String] = source.runWith(sinkFold)
res2
}
yScale.domain(
[d3.max(dataset) + (
10 - (
d3.max(dataset) % 10 )
)
), 0]