Home > Software design >  How can i handle multipart post request with akka http?
How can i handle multipart post request with akka http?

Time:07-31

I wont to handle multipart request. If I accept a request using such a route

   val routePutData = path("api" / "putFile" / Segment) {
      subDir => {
        entity(as[String]) { (str) => {
          complete(str)
        }
      }
    }}

I get the following text(i try to send log4j config):

Content-Disposition: form-data; name="file"; filename="log4j.properties"
Content-Type: application/binary

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %t %-5p %c{1} - %m%n
----gc0pMUlT1B0uNdArYc0p--

How can i get array of bytes from file i send and file name? I try to use entity(as[Multipart.FormData]), and formFields directive, but it didn't help.

CodePudding user response:

You should keep up with the akka docs, but I think that there were not enought examples in the file uploading section. Anyway, you don't need to extract entity as a string or byte arrays, akka already has a directive, called fileUpload. This takes a parameter called fieldName which is the key to look for in the multipart request, and expects a function to know what to do given the metadata and the content of the file. Something like this:

post {
  extractRequestContext { ctx =>
    implicit val mat = ctx.materializer

    fileUpload(fieldName = "myfile") {
      case (metadata, byteSource) =>

        val fileName = metadata.fileName
        val futureBytes = byteSource
          .mapConcat[Byte] { byteString =>
            collection.immutable.Iterable.from(
              byteString.iterator
            )
          }
          .toMat(Sink.fold(Array.emptyByteArray) {
            case (arr, newLine) => arr :  newLine
          }
          )(Keep.right)
          .run()

        val filePath = Files.createFile(Paths.get(s"/DIR/TO/SAVE/FILE/$fileName"))
        onSuccess(futureBytes.map(bytes => Files.write(filePath, bytes))) { _ =>
          complete(s"wrote file to: ${filePath.toUri.toString}")
        }

    }
  }
}
  • Related