Scala 如何从akka源[ByteString,\]中提取未来的[String]?
我试图使用akka streams来流式传输文件,但遇到了一个小问题,将流的结果提取到未来的[String]:Scala 如何从akka源[ByteString,\]中提取未来的[String]?,scala,akka,akka-http,akka-stream,Scala,Akka,Akka Http,Akka Stream,我试图使用akka streams来流式传输文件,但遇到了一个小问题,将流的结果提取到未来的[String]: def streamMigrationFile(source: Source[ByteString, _]): Future[String] = { var fileString = "" val sink = Sink.foreach[ByteString](byteString => fileString = fileString.concat(byteSt
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]
有人能帮我理解我做错了什么,以及我需要做什么来提取流的结果吗?如果我猜的是对的,那么您希望将整个文件内容流成一个字符串。这最好使用
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)
}
您可能知道这一点,但您的文件需要放入内存中,程序才能正确运行。如果您查看
Sink的定义。foreach
您会发现计算类型为Sink[T,Future[Done]]
这意味着流中元素的计算结果将发生什么并不重要。定义如下:
def foreach[T](f: T ⇒ Unit): Sink[T, Future[Done]]
另一方面,Sink.fold
的定义计算为Future[U]
是U
零的类型。换句话说,您可以在处理结束时定义未来的类型
以下是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
s(或者在您的情况下是ByteString
),我(流)将给您U
s(或者在你的情况下,String
)…当我完成时(在未来
)”
下面是一个使用Sink.fold
替换Sink.foreach
并将整个表达式计算为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
}
你介意再解释一下用例吗?最终的未来将用于什么?