I have not been able to find any way to marshall a set of HTTP Post form 
parameters AND a file using akka-http, where the form data parameters are 
NOT www-url-encoded, but rather each placed in a separate MIME part, with 
no Content-Type header. This appears to be the format required in order to 
do an HTTP POST to AWS S3 with a policy.  Furthermore, it appears AWS/S3 is 
not happy with Chunked HTTP POST requests, and therefore I've had to use 
toStrict on all my entities.  I've resorted to some hacky code, which 
works, but I find it surprising that there is no built-in way to generate a 
multi-part MIME message where post parameters are not combined into one 
www-url-encoded query string.  

Here is my (admittedly ugly) code:

    val m = Map(

      "key" -> "upload/quux.txt",

      "acl" -> acl,

      "policy" -> policy,

      "X-amz-algorithm" -> "AWS4-HMAC-SHA256",

      "X-amz-credential" -> s"$awsAccessKeyId/$shortDate/$region/$service
/aws4_request",

      "X-amz-date" -> date,

      "X-amz-expires" -> expires.toString,

      "X-amz-signature" -> signature

    )

    

    implicit val timeout: FiniteDuration = 10.seconds

    

    val uri = s"http://$bucket.$service-$region.amazonaws.com";

    

    // create request entities for each of the POST parameters. Note that 
generating Strict entities is an asynchronous

    // operation (returns a future). Strict entities are NOT chunked. It 
appears AWS is not happy with chunked MIME

    // body parts.

    val strictFormDataEntityFuture = m.map { case (k, v) => (k, 
HttpEntity(ContentTypes.NoContentType, 
v).toStrict(timeout)) }

    

    val responseFuture = for {

      strictEntityMap <- Future.sequence(strictFormDataEntityFuture.map(
entry => entry._2.map(i => (entry._1, i)))).map(_.toMap) recoverWith { case 
t: Throwable => throw t }

      formDataBodyPartList = strictEntityMap.map {e => 
Multipart.FormData.BodyPart.Strict(e._1, e._2)}.toList

      fileEntity <- HttpEntity(ContentTypes.`application/octet-stream`, 3, 
Source.single(ByteString("foo"))).toStrict(timeout) recoverWith { case t: 
Throwable => throw t }

      multipartForm = Multipart.FormData(Source(formDataBodyPartList ++List
(Multipart.FormData.BodyPart("file", fileEntity, Map("filename" -> "foo.bin"
)))))

      requestEntity <- Marshal(multipartForm).to[RequestEntity] recoverWith 
{ case t: Throwable => throw t }

      strictRequestEntity <- requestEntity.toStrict(timeout) recoverWith { 
case t: Throwable => throw t }

      response <- Http().singleRequest(HttpRequest(method = 
HttpMethods.POST, uri = uri, entity = strictRequestEntity)) recoverWith { 
case t: Throwable => throw t }

      responseEntity <- Unmarshal(response.entity).to[String] recoverWith { 
case t: Throwable => throw t }

    } yield (responseEntity, response)


If anyone can suggest a better approach, I'd appreciate it.  Thanks. 

On Sunday, October 11, 2015 at 10:45:37 PM UTC-7, Eric Swenson wrote:
>
> Hey Scott!
>
> When you get a chance, if you can point me to the right one, I'd 
> appreciate it. I've been looking at the sources, tests, and examples, and I 
> can't find how to do it.  What I'm after is a way to take a Form, and emit 
> a multi-part MIME message with one part for each field in the form and one 
> part for the single file I need to upload. Using code like this:
>
>      val formBodyPartFuture = for {
>
>       httpEntity <- Marshal(form).to[HttpEntity]
>
>       strictEntity <- httpEntity.toStrict(timeout)
>
>     } yield Multipart.FormData.BodyPart("foo", strictEntity)
>
>
> gives me one body part with Content-Type x-www-url-encoded, and the set of 
> all fields are encoded as in a query string.If you can point me to the 
> "other" way of marshalling a form to a set of mime body parts, I'd 
> appreciate it. I had pretty much given up (and googling and using stack 
> overflow have turned up nothing) and was going to generate the parts 
> manually.  i'm sure there must be a correct way to do this.  Thanks. -- 
>  Eric
>
>
>
>
> On Sunday, October 11, 2015 at 9:43:20 PM UTC-7, Scott Maher wrote:
>>
>> I can't test this as I am not at home but there are multiple form 
>> marshallers, one for url encoding and one for multipart called, I think, 
>> Multipart.FormData. sorry if you already know this.
>>
>> Hi Eric! :P
>> On Oct 11, 2015 9:18 PM, "Eric Swenson" <er...@swenson.org> wrote:
>>
>>> AWS/S3 HTTP Post with a policy requires an HTTP post with multipart-mime 
>>> -- one part for the file and one part, each, for various form parameters. 
>>> When I use akka.http and attempt to marshal FormData to an Entity and use 
>>> that to create a multi-part mime message, all the form data go into one 
>>> mime-part using the query string format (foo=bar&baz=quux). AWS rejects 
>>> such a request as it doesn't want all form parameters in a single 
>>> www-url-encoded-form-data part, but rather separate mime parts for each 
>>> parameter. How to I cause a Form to be martialled in this way using 
>>> akka.http?
>>>
>>> -- 
>>> >>>>>>>>>> Read the docs: http://akka.io/docs/
>>> >>>>>>>>>> Check the FAQ: 
>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>> >>>>>>>>>> Search the archives: 
>>> https://groups.google.com/group/akka-user
>>> --- 
>>> You received this message because you are subscribed to the Google 
>>> Groups "Akka User List" group.
>>> To unsubscribe from this group and stop receiving emails from it, send 
>>> an email to akka-user+...@googlegroups.com.
>>> To post to this group, send email to akka...@googlegroups.com.
>>> Visit this group at http://groups.google.com/group/akka-user.
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>

-- 
>>>>>>>>>>      Read the docs: http://akka.io/docs/
>>>>>>>>>>      Check the FAQ: 
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>>      Search the archives: https://groups.google.com/group/akka-user
--- 
You received this message because you are subscribed to the Google Groups "Akka 
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to akka-user+unsubscr...@googlegroups.com.
To post to this group, send email to akka-user@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to