使用akka-http websockets上传和处理文件

时间:2017-07-14 08:29:54

标签: scala websocket akka akka-http

我使用一些示例Scala代码创建一个通过websocket接收文件的服务器,临时存储该文件,在其上运行bash脚本,然后通过TextMessage返回stdout。

示例代码来自this github project

我在echoService中略微编辑了代码,以便它运行另一个处理临时文件的函数。

object WebServer {
  def main(args: Array[String]) {

    implicit val actorSystem = ActorSystem("akka-system")
    implicit val flowMaterializer = ActorMaterializer()

    val interface = "localhost"
    val port = 3000

    import Directives._

    val route = get {
      pathEndOrSingleSlash {
        complete("Welcome to websocket server")
      }
    } ~
      path("upload") {
        handleWebSocketMessages(echoService)
      }

      val binding = Http().bindAndHandle(route, interface, port)
      println(s"Server is now online at http://$interface:$port\nPress RETURN to stop...")
      StdIn.readLine()

      binding.flatMap(_.unbind()).onComplete(_ => actorSystem.shutdown())
      println("Server is down...")

    }

    implicit val actorSystem = ActorSystem("akka-system")
    implicit val flowMaterializer = ActorMaterializer()


    val echoService: Flow[Message, Message, _] = Flow[Message].mapConcat {

      case BinaryMessage.Strict(msg) => {
        val decoded: Array[Byte] = msg.toArray
        val imgOutFile = new File("/tmp/" + "filename")
        val fileOuputStream = new FileOutputStream(imgOutFile)
        fileOuputStream.write(decoded)
        fileOuputStream.close()
        TextMessage(analyze(imgOutFile))
      }

      case BinaryMessage.Streamed(stream) => {

        stream
          .limit(Int.MaxValue) // Max frames we are willing to wait for
          .completionTimeout(50 seconds) // Max time until last frame
          .runFold(ByteString(""))(_ ++ _) // Merges the frames
          .flatMap { (msg: ByteString) =>

          val decoded: Array[Byte] = msg.toArray
          val imgOutFile = new File("/tmp/" + "filename")
          val fileOuputStream = new FileOutputStream(imgOutFile)
          fileOuputStream.write(decoded)
          fileOuputStream.close()
          Future(Source.single(""))
        }
        TextMessage(analyze(imgOutFile))
      }


      private def analyze(imgfile: File): String = {
        val p = Runtime.getRuntime.exec(Array("./run-vision.sh", imgfile.toString))
        val br = new BufferedReader(new InputStreamReader(p.getInputStream, StandardCharsets.UTF_8))
        try {
          val result = Stream
            .continually(br.readLine())
            .takeWhile(_ ne null)
            .mkString
          result

        } finally {
          br.close()
        }
      }
    }




}

在使用Dark WebSocket终端进行测试期间,case BinaryMessage.Strict工作正常。

问题:但是,case BinaryMessage.Streaming在运行analyze函数之前没有完成文件的写入,导致服务器返回空白。

我试图通过Akka-HTTP中的Flows来探讨Futures的使用方式,但我在尝试通过所有官方文档之前没有太多运气。

目前,.mapAsync看起来很有希望,或基本上找到了连锁期货的方法。

我非常欣赏一些见解。

1 个答案:

答案 0 :(得分:2)

是的,mapAsync会在这个场合为您提供帮助。它是一个组合器,可以在流中执行Future(可能并行),并在输出端显示它们的结果。

在你的情况下,为了使事物变得同质并使类型检查器满意,你需要将Strict案例的结果包装成Future.successful

您的代码的快速修复可能是:

  val echoService: Flow[Message, Message, _] = Flow[Message].mapAsync(parallelism = 5) {

    case BinaryMessage.Strict(msg) => {
      val decoded: Array[Byte] = msg.toArray
      val imgOutFile = new File("/tmp/" + "filename")
      val fileOuputStream = new FileOutputStream(imgOutFile)
      fileOuputStream.write(decoded)
      fileOuputStream.close()
      Future.successful(TextMessage(analyze(imgOutFile)))
    }

    case BinaryMessage.Streamed(stream) =>

      stream
        .limit(Int.MaxValue) // Max frames we are willing to wait for
        .completionTimeout(50 seconds) // Max time until last frame
        .runFold(ByteString(""))(_ ++ _) // Merges the frames
        .flatMap { (msg: ByteString) =>

        val decoded: Array[Byte] = msg.toArray
        val imgOutFile = new File("/tmp/" + "filename")
        val fileOuputStream = new FileOutputStream(imgOutFile)
        fileOuputStream.write(decoded)
        fileOuputStream.close()
        Future.successful(TextMessage(analyze(imgOutFile)))
      }
  }