当前位置: 首页 > 知识库问答 >
问题:

如何从 akka Source[ByteString, _] 中提取 Future[String]?

洪高扬
2023-03-14

我正在尝试使用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]

任何人都可以帮助我了解我做错了什么,以及我需要做些什么来提取流的结果?

共有2个答案

阴焱
2023-03-14

如果您查看Sink.foreach的定义,您会发现评估类型是Sink[T,Future[Done]],这意味着流中元素的计算结果会发生什么并不重要。定义如下:

def foreach[T](f: T ⇒ Unit): Sink[T, Future[Done]]

另一方面,Sink的定义。折叠计算为未来[U],即U0的类型。换句话说,您可以在处理结束时定义未来的类型。

以下是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")

根据上面的实现,您可以看到要在具体化中保存的类型是< code>Future[U],因为< code>Keep.right意味着类似于这样的内容:“我不在乎传入的元素是< code>Ts(或者在您的情况下是< code > ByteString )I(流)将给您< code>Us(或者在您的情况下是< code>String)..当我完成时(在< code >未来)”

以下是替换<code>接收器的工作示例。foreach带有接收器。折叠,并将整个表达式求值为未来[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

}
夏侯渊
2023-03-14

如果我的猜测是正确的,您希望将整个文件内容流式传输到字符串中。这最好通过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)
}

您可能已经意识到了这一点,但是您的文件需要放入内存中才能使程序正常运行。

 类似资料:
  • 我想在我的项目中使用谷歌protobuf。 关键是我必须设置每个消息的第一个字节,因为底层代码拒绝或接受基于第一个字节的消息,而它不知道protobuf。 所以这页说https://developers.google.com/protocol-buffers/docs/proto#scalar我必须使用与Java中的ByteString相对应的bytes字段。 字节可以包含任意字节序列。字符串By

  • 问题内容: 我有一个网页,可以用来上传文件。 现在,我需要检查文件类型是否正确(zip,jpg,pdf,…)。 我可以使用请求随附的mimeType,但我不信任用户,并且说我想确保没有人能够上传在.jpg中重命名的.gif文件。 我认为在这种情况下,我应该检查魔术号码。我发现 这是一个Java库,似乎可以实现“从魔术数字中提取模仿类型”的功能。 这是正确的解决方案还是您建议什么? 更新: 我已经找

  • 看到截图了吗 从图像解析器中可以看到,它返回NP,VP,PP,NP。我希望能够访问不同深度的所有短语。例如,in depth=1有两个短语NP和VP,in depth=2有一些其他短语,in depth=3有一些其他短语。如何使用python访问属于depth=n的短语?

  • 问题内容: 我需要在运行时复制一个打包在Jar中的文件夹。我想通过在同样包含在同一文件夹中的类中调用函数来实现。 我试过使用: 但这不起作用。我可能必须递归使用函数。有没有更优雅/前瞻的方式来做到这一点? 万一我必须递归地做它:1.我不想指定硬编码的文件,我想动态地做它2.我不想创建一个单独的档案。我希望此资源与处理该资源的类位于同一个Jar中 谢谢 我最后做了Koziołek的建议。尽管我希望有

  • 我有一个Excel工作表,其中一栏填充了专利号。我需要提取每个相应专利的标题,并将其放在专利号旁边的单元格中。因此,代码应执行以下操作: 访问espacenet.com并打开需要名称的专利号。 获取标题。 将其放在所需单元格的Excel工作表中。 这是一个完美适用于第一个专利号的代码,但在这之后会立即出现错误。错误显示:“运行时错误'-2147417848(80010108)': 自动化错误调用的