4

I am a little new to akka world, so my domain of knowledge is a bit small. I am creating an https server and handling it using akka streams and http, for a specific url, i need to send a file back to client. How can i achieve that using akka streams and avoiding the akka routes.

def handleCall(request:HttpRequest):HttpResponse = {
  logger.info("Request is {}",request)
  val uri:String = request.getUri().path()
  if(uri == "/download"){
    val f = new File("/1000.txt")
    logger.info("file download")
    return HttpEntity(
    //What should i put here if i want to return a text file.
    )
}
Saksham
  • 127
  • 3
  • 9

3 Answers3

6

If the file is likely to be large then you don't want to consume the entire contents into memory before sending it to the client. This is solved via a purely stream based solution:

import scala.io
import akka.stream.scaladsl.Source
import akka.http.scaladsl.model.HttpEntity.{Chunked, ChunkStreamPart}
import akka.http.scaladsl.model.{HttpResponse, ContentTypes}

val fileContentsSource : (String, String) => Source[ChunkStreamPart, _] =
  (fileName, enc) =>
    Source
      .fromIterator( io.Source.fromFile(fileName, enc).getLines )
      .map(ChunkStreamPart.apply)


val fileEntityResponse : (String, String) => HttpResponse =
  (fileName, enc) => 
    HttpResponse(entity = Chunked(ContentTypes.`text/plain(UTF-8)`,
                                  fileContentsSource(fileName, enc)))

Now you can create and send an HttpResponse without having the server keep the entire contents:

val httpResp : HttpResponse = fileEntityResponse("/foo/log.txt", "UTF8")
Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
  • Sorry for jumping on this, but any ideas why this wouldn't be returning any response to client, I'm getting a Error: read ECONNRESET in Postman, I see no errors in logs either? complete(HttpResponse(entity = Chunked(ContentTypes.`application/octet-stream`, fileEntityResponse("/foo/my-video.mp4", "UTF8")))) – Rory Aug 23 '18 at 14:26
  • @Rory No apologies necessary. Perhaps there is an issue with reading an mp4 file using UTF-8 encoding. However, that's just a guess as I am unfamiliar with "ECONNRESET"... – Ramón J Romero y Vigil Aug 23 '18 at 14:30
2

Akka Http Route

  pathSingleSlash {
    get {
      complete(HttpEntity.fromFile(MediaTypes.`application/zip`, new File(s"/home/shivam/sample.zip"))
    }
  }

Curl Request

curl --output sample.zip http://localhost:8080/

Copied from HttpEntity.scala

 /**
   * Returns either the empty entity, if the given file is empty, or a [[HttpEntity.Default]] entity
   * consisting of a stream of [[akka.util.ByteString]] instances each containing `chunkSize` bytes
   * (except for the final ByteString, which simply contains the remaining bytes).
   *
   * If the given `chunkSize` is -1 the default chunk size is used.
   */
iamsmkr
  • 800
  • 2
  • 10
  • 29
1
val str2 = scala.io.Source.fromFile("/tmp/t.log", "UTF8").mkString
val str = Source.single(ByteString(str2))
HttpResponse(entity = HttpEntity.Chunked.fromData(ContentTypes.`application/octet-stream`, str))
Knows Not Much
  • 30,395
  • 60
  • 197
  • 373
  • I believe this solution will read the entire contents of the file into memory before constructing the `HttpEntity`. Therefore, a `Strict` entity would make more sense than a `Chunked`. Also, this now negates the benefits of "streaming" the file contents to the client since no real streaming is involved... – Ramón J Romero y Vigil Aug 23 '18 at 14:43