diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala index d036a992a6..b107cafef1 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala @@ -4,6 +4,8 @@ package akka.http.scaladsl.model +import java.io.File + import akka.event.{ NoLogging, LoggingAdapter } import scala.collection.immutable.VectorBuilder @@ -12,6 +14,7 @@ import scala.concurrent.{ Future, ExecutionContext } import scala.collection.immutable import scala.util.{ Failure, Success, Try } import akka.stream.FlowMaterializer +import akka.stream.io.SynchronousFileSource import akka.stream.scaladsl.{ FlattenStrategy, Source } import akka.http.scaladsl.util.FastFuture import akka.http.scaladsl.model.headers._ @@ -174,6 +177,7 @@ object Multipart { } object FormData { def apply(parts: BodyPart.Strict*): Strict = Strict(parts.toVector) + def apply(parts: BodyPart*): FormData = FormData(Source(parts.toVector)) def apply(fields: Map[String, HttpEntity.Strict]): Strict = Strict { fields.map { case (name, entity) ⇒ BodyPart.Strict(name, entity) }(collection.breakOut) @@ -184,6 +188,15 @@ object Multipart { override def toString = s"FormData($parts)" } + /** + * Creates a FormData instance that contains a single part backed by the given file. + * + * To create an instance with several parts or for multiple files, use + * ``FormData(BodyPart.fromFile("field1", ...), BodyPart.fromFile("field2", ...)`` + */ + def fromFile(name: String, contentType: ContentType, file: File, chunkSize: Int = SynchronousFileSource.DefaultChunkSize): FormData = + FormData(Source.single(BodyPart.fromFile(name, contentType, file, chunkSize))) + /** * Strict [[FormData]]. */ @@ -221,6 +234,12 @@ object Multipart { override def toString = s"FormData.BodyPart($name, $entity, $additionalDispositionParams, $additionalHeaders)" } + /** + * Creates a BodyPart backed by a File that will be streamed using a SynchronousFileSource. + */ + def fromFile(name: String, contentType: ContentType, file: File, chunkSize: Int = SynchronousFileSource.DefaultChunkSize): BodyPart = + BodyPart(name, HttpEntity(contentType, file, chunkSize), Map("filename" -> file.getName)) + def unapply(value: BodyPart): Option[(String, BodyPartEntity, Map[String, String], immutable.Seq[HttpHeader])] = Some((value.name, value.entity, value.additionalDispositionParams, value.additionalHeaders))