简体   繁体   中英

How can i handle multipart post request with akka http?

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.

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

    }
  }
}

While the above solution looks good, there is also the storeUploadedFile directive to achieve the same with less code, sth like:

  path("upload") {
      def tempDestination(fileInfo: FileInfo): File = File.createTempFile(fileInfo.fileName, ".tmp.server")

      storeUploadedFile("myfile", tempDestination) {
        case (metadataFromClient: FileInfo, uploadedFile: File) =>
          println(s"Server stored uploaded tmp file with name: ${uploadedFile.getName} (Metadata from client: $metadataFromClient)")
          complete(HttpResponse(StatusCodes.OK))
      }
  }

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