Scala 如何从akka源[ByteString,\]中提取未来的[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

我试图使用akka streams来流式传输文件,但遇到了一个小问题,将流的结果提取到未来的[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]

有人能帮我理解我做错了什么,以及我需要做什么来提取流的结果吗?

如果我猜的是对的,那么您希望将整个文件内容流成一个字符串。这最好使用
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

}

你介意再解释一下用例吗?最终的未来将用于什么?