简体   繁体   中英

Provide a file via REST api using Akka HTTP

I am writing an application to provide files to the clients via a RESTful service for which I am using Akka HTTP.

The request from the client could be a POST request with the body containing the name of the file which I need to provide.

{filename: abc.zip}

I have the files stored on my server. How do I go about providing the file and what would be the appropriate Response type? The file could be of any format. In java, we specify it as MediaType.APPLICATION_OCTET_STREAM.

A barebone HTTP server for binary file download (with the target file located at src/main/resources/ ) might look something like the following:

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())
  }
}

To test it out, just run the server and use cURL on a command prompt like below:

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]

Per questions in comment section, formField() is for extracting a single Form field in the request. You could also use entity(as[someType]) instead by replacing line formField('filename) ... => with the following:

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

FileIO.fromPath(nioFilePath, chunkSize) allows you to provide a buffer size, which is 100,000 in the sample code. You could also create custom Akka Flow with Framing.delimiter() for more complex requirement. More details can be found in relevant Akka doc .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM