Saurabh
Saurabh

Reputation: 73589

How to send a file and json payload together in Post Request with akka-http

I have to send following curl POST request in my akka code where it is sending both json payload and file in the request:

curl \
  -F "payload=</tmp/upload_file_payload.json" \
  -F "file=@/tmp/file.pdf" \
   -v https://host/api

I am trying to implement the same in akka-http, but not sure exactly how to do it. I found some example here and here, but it is not working as it is, so I have written following code, which also have some error, but seems like I am close:

val httpEntity = HttpEntity(MediaTypes.`application/octet-stream`, file, 100000)
//    val httpEntity = HttpEntity.fromPath(ContentType.apply(MediaTypes.`application/octet-stream`), Paths.get(file.getAbsolutePath))
val fileFormData = Multipart.FormData.BodyPart.Strict("file", httpEntity, Map.empty)
val jsonFormData = Multipart.FormData.BodyPart.Strict("payload", payload, Map.empty)


//      Multipart.FormData.Strict(scala.collection.immutable.Seq(jsonFormData, fileFormData)).toEntity()
val entity = Multipart.FormData( Source(List(jsonFormData, fileFormData))).toEntity()
val httpRequest = HttpRequest(HttpMethods.POST, uri = uri, entity = entity)

But this code is not compiling.

In between, when I made the code compile, I was getting error:

411 Length Requered

I tried added Content-Length header with request but no avail.

Upvotes: 2

Views: 953

Answers (1)

Saurabh
Saurabh

Reputation: 73589

Finally after looking at the akka-http code and more attempts, finally I got this working as following, explanation in comments:

val httpEntity = HttpEntity(MediaTypes.`application/octet-stream`, file, 100000).toStrict(10.seconds)(mat)   // I had to convert this into strict: which adds Content-Length and tells it is not streaming
val fileFormData = Multipart.FormData.BodyPart.Strict("file", Await.result(httpEntity, 10.seconds), Map.empty)  // used Await here to get httpEntity from Future, this might be made better
val jsonFormData = Multipart.FormData.BodyPart.Strict("payload", payload, Map.empty)

val entity = Multipart.FormData(jsonFormData, fileFormData).toEntity() // Corrected this signature
val httpRequest = HttpRequest(HttpMethods.POST, uri = uri, entity = entity)

To avoid 411 error I had to make FormData strict, whcih itself adds required Content-Length header.

Upvotes: 1

Related Questions