使用Akka HTTP通过REST api提供文件

时间:2018-02-03 15:52:40

标签: scala rest akka akka-http

我正在编写一个应用程序,通过我使用Akka HTTP的RESTful服务向客户端提供文件。

来自客户端的请求可能是一个POST请求,正文包含我需要提供的文件的名称。

{filename: abc.zip}

我的文件存储在我的服务器上。我如何提供文件以及适当的响应类型?该文件可以是任何格式。在java中,我们将其指定为MediaType.APPLICATION_OCTET_STREAM。

1 个答案:

答案 0 :(得分:1)

用于二进制文件下载的裸机HTTP服务器(目标文件位于src/main/resources/)可能如下所示:

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpEntity, ContentTypes}
import akka.http.scaladsl.server.Directives._
import java.nio.file.Paths
import scala.io.StdIn

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

    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()
    implicit val ec = system.dispatcher

    val route =
      path("hello") {
        get {
          complete( HttpEntity( ContentTypes.`text/html(UTF-8)`,
            "Hello from Akka-HTTP!"
          ) )
        }
      } ~
      path("download") {
        post {
          formField('filename) { filename: String =>
            complete( HttpEntity( ContentTypes.`application/octet-stream`,
              FileIO.fromPath(Paths.get(s"src/main/resources/$filename"), 100000)
            ) )
          }
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

    println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
    StdIn.readLine()

    bindingFuture
      .flatMap(_.unbind())
      .onComplete(_ => system.terminate())
  }
}

要测试它,只需运行服务器并在命令提示符下使用cURL,如下所示:

curl -X GET http://localhost:8080/hello
// Hello from Akka-HTTP!

curl -d "filename=abc.zip" -X POST http://localhost:8080/download > abc1.zip
//   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
//                                  Dload  Upload   Total   Spent    Left  Speed
// 100  131k    0  131k  100    16  6914k    839 --:--:-- --:--:-- --:--:-- 6937k

[UPDATE]

评论部分中的每个问题,formField()用于提取请求中的单个表单字段。您也可以使用entity(as[someType])替换行formField('filename) ... =>替换为以下内容:

      entity(as[String]) { entity =>
        val filename = entity.split("=").last

FileIO.fromPath(nioFilePath, chunkSize)允许您提供缓冲区大小,示例代码中为100,000。您还可以使用Framing.delimiter()创建自定义Akka Flow以满足更复杂的要求。有关详情,请参阅相关的Akka doc