Скачать указанный файл с потоками Акка?
Для следующего кода мне нужно, чтобы клиент отправил имя файла на сервер, чтобы сервер мог ответить клиенту на содержимое указанного файла. Это может сработать, если я не укажу имя файла, просто жесткое кодирование в серверной части, но как клиент скажет серверу, что ему нужен указанный файл?
Server.scala
package com.tst
import java.nio.file.Paths
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Tcp.{IncomingConnection, ServerBinding}
import akka.stream.scaladsl._
import akka.util.ByteString
import scala.concurrent.Future
object Server extends App {
implicit val system = ActorSystem()
implicit val ec = system.dispatcher
implicit val materializer = ActorMaterializer()
val connections: Source[IncomingConnection, Future[ServerBinding]] =
Tcp().bind("127.0.0.1", 9989)
connections runForeach { connection =>
println(s"New connection from: ${connection.remoteAddress}")
var fileName = ""
// NOTE: here, the fileName = item can not affect the latter runWith,
// I want to find solution
val go = Flow[ByteString].via(connection.flow).map(_.utf8String).map {
item => println(item); fileName = item
}.runWith(FileIO.fromPath(Paths.get(fileName)), Sink.ignore)
}
}
Client.scala
package com.tst
import java.nio.file.Paths
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.util.ByteString
object Client extends App {
implicit val system = ActorSystem()
implicit val ec = system.dispatcher
implicit val materializer = ActorMaterializer()
val connection = Tcp().outgoingConnection("127.0.0.1", 9989)
val f = Source(List("D:/testinput.txt")).map(ByteString(_)).via(connection).
runWith(FileIO.toPath(Paths.get("D:/testoutput.txt")))
f.foreach {
_ =>
println("done")
system.terminate()
}
}
build.sbt
name := "streamtest"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-stream" % "2.4.20"
)
1 ответ
На стороне сервера вы можете использовать handleWith
метод для Connection
в сочетании с Flow
это берет в имени файла и производит строки файла:
import akka.stream.scaladsl.FileIO
import java.nio.file.Paths
val fileNameToContentsFlow : Flow[ByteString, ByteString, _] =
Flow[ByteString]
.map(_.utf8String)
.take(1L)
.map(fileName => Paths.get(fileName))
.flatMapConcat( filePath => FileIO.fromPath(filePath) )
Примечание: я добавил .take(1L)
так что клиент может получить доступ только к 1 файлу на каждое соединение. Это может быть изменено для обработки нескольких файлов на одно соединение, но тогда необходимо будет вставить соответствующий разделитель между содержимым каждого отдельного файла.
Ваш код сервера будет немного изменен
connections runForeach { connection =>
connection.handleWith(fileNameToContentsFlow)
}