+htt 16841 uploadedFile directive

Two new directives for accepting file uploads through multipart forms:

`uploadedFile` which allows for very simple upload into a temporary file
`fileUpload` which allows to simply work with the stream of bytes of an upload
This commit is contained in:
Johan Andrén 2015-11-17 12:25:58 +01:00
parent 35b690371f
commit 3c0877d964
10 changed files with 401 additions and 0 deletions

View file

@ -0,0 +1,81 @@
/**
* Copyright (C) 2015 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server.directives
import java.io.File
import akka.http.scaladsl.model.{ MediaTypes, HttpEntity, Multipart, StatusCodes }
import akka.stream.io.Framing
import akka.util.ByteString
import docs.http.scaladsl.server.RoutingSpec
import scala.concurrent.Future
import scala.util.{ Success, Failure }
class FileUploadDirectivesExamplesSpec extends RoutingSpec {
override def testConfigSource = "akka.actor.default-mailbox.mailbox-type = \"akka.dispatch.UnboundedMailbox\""
"uploadedFile" in {
val route =
uploadedFile("csv") {
case (metadata, file) =>
// do something with the file and file metadata ...
file.delete()
complete(StatusCodes.OK)
}
// tests:
val multipartForm =
Multipart.FormData(
Multipart.FormData.BodyPart.Strict(
"csv",
HttpEntity(MediaTypes.`text/plain`, "1,5,7\n11,13,17"),
Map("filename" -> "data.csv")))
Post("/", multipartForm) ~> route ~> check {
status shouldEqual StatusCodes.OK
}
}
"fileUpload" in {
// adding integers as a service ;)
val route =
extractRequestContext { ctx =>
implicit val mat = ctx.materializer
implicit val ec = ctx.executionContext
fileUpload("csv") {
case (metadata, byteSource) =>
val sumF: Future[Int] =
// sum the numbers as they arrive so that we can
// accept any size of file
byteSource.via(Framing.delimiter(ByteString("\n"), 1024))
.mapConcat(_.utf8String.split(",").toVector)
.map(_.toInt)
.runFold(0) { (acc, n) => acc + n }
onSuccess(sumF) { sum => complete(s"Sum: $sum") }
}
}
// tests:
val multipartForm =
Multipart.FormData(Multipart.FormData.BodyPart.Strict(
"csv",
HttpEntity(MediaTypes.`text/plain`, "2,3,5\n7,11,13,17,23\n29,31,37\n"),
Map("filename" -> "primes.csv")))
Post("/", multipartForm) ~> route ~> check {
status shouldEqual StatusCodes.OK
responseAs[String] shouldEqual "Sum: 178"
}
}
}

View file

@ -62,6 +62,7 @@ Directive Description
:ref:`-extractUri-` Extracts the complete request URI
:ref:`-failWith-` Bubbles the given error up the response chain where it is dealt with by the
closest :ref:`-handleExceptions-` directive and its ``ExceptionHandler``
:ref:`-fileUpload-` Provides a stream of an uploaded file from a multipart request
:ref:`-formField-` Extracts an HTTP form field from the request
:ref:`-formFields-` Extracts a number of HTTP form field from the request
:ref:`-get-` Rejects all non-GET requests
@ -199,6 +200,7 @@ Directive Description
:ref:`-setCookie-` Adds a ``Set-Cookie`` response header with the given cookies
:ref:`-textract-` Extracts a number of values using a ``RequestContext ⇒ Tuple`` function
:ref:`-tprovide-` Injects a given tuple of values into a directive
:ref:`-uploadedFile-` Streams one uploaded file from a multipart request to a file on disk
:ref:`-validate-` Checks a given condition before running its inner route
:ref:`-withExecutionContext-` Runs its inner route with the given alternative ``ExecutionContext``
:ref:`-withMaterializer-` Runs its inner route with the given alternative ``Materializer``

View file

@ -41,6 +41,9 @@ Directives filtering or extracting from the request
:ref:`BasicDirectives` and :ref:`MiscDirectives`
Directives handling request properties.
:ref:`FileUploadDirectives`
Handle file uploads.
.. _Response Directives:
@ -85,6 +88,7 @@ List of predefined directives by trait
debugging-directives/index
execution-directives/index
file-and-resource-directives/index
file-upload-directives/index
form-field-directives/index
future-directives/index
header-directives/index

View file

@ -0,0 +1,25 @@
.. _-fileUpload-:
fileUpload
==========
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala
:snippet: fileUpload
Description
-----------
Simple access to the stream of bytes for a file uploaded as a multipart form together with metadata
about the upload as extracted value.
If there is no field with the given name the request will be rejected, if there are multiple file parts
with the same name, the first one will be used and the subsequent ones ignored.
Example
-------
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala
:snippet: fileUpload

View file

@ -0,0 +1,10 @@
.. _FileUploadDirectives:
FileUploadDirectives
====================
.. toctree::
:maxdepth: 1
uploadedFile
fileUpload

View file

@ -0,0 +1,31 @@
.. _-uploadedFile-:
uploadedFile
============
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala
:snippet: uploadedFile
Description
-----------
Streams the contents of a file uploaded as a multipart form into a temporary file on disk and provides the file and
metadata about the upload as extracted value.
If there is an error writing to disk the request will be failed with the thrown exception, if there is no field
with the given name the request will be rejected, if there are multiple file parts with the same name, the first
one will be used and the subsequent ones ignored.
.. note::
This directive will stream contents of the request into a file, however one can not start processing these
until the file has been written completely. For streaming APIs it is preferred to use the :ref:`-fileUpload-`
directive, as it allows for streaming handling of the incoming data bytes.
Example
-------
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala
:snippet: uploadedFile

View file

@ -77,6 +77,7 @@ in the :ref:`exception-handling-scala` section of the documtnation. You can use
File uploads
^^^^^^^^^^^^
For high level directives to handle uploads see the :ref:`FileUploadDirectives`.
Handling a simple file upload from for example a browser form with a `file` input can be done
by accepting a `Multipart.FormData` entity, note that the body parts are `Source` rather than