Eric,

you can create a multipart/formdata request like this, for example:

    val formData = {
      def utf8TextEntity(content: String) = {
        val bytes = ByteString(content)
        HttpEntity.Default(ContentTypes.`text/plain(UTF-8)`, bytes.length, 
Source.single(bytes))
      }
      
      import Multipart._
      FormData(Source(
        FormData.BodyPart("foo", utf8TextEntity("FOO")) ::
          FormData.BodyPart("bar", utf8TextEntity("BAR")) :: Nil))
    }

    val request = HttpRequest(POST, entity = formData.toEntity())

This will render a chunked request that looks something like this:

    POST / HTTP/1.1
    Host: example.com
    User-Agent: akka-http/test
    Transfer-Encoding: chunked
    Content-Type: multipart/form-data; boundary=o5bjIygPrPrCCYUp8FyfvsQe

    71
    --o5bjIygPrPrCCYUp8FyfvsQe
    Content-Type: text/plain; charset=UTF-8
    Content-Disposition: form-data; name=foo


    3
    FOO
    73

    --o5bjIygPrPrCCYUp8FyfvsQe
    Content-Type: text/plain; charset=UTF-8
    Content-Disposition: form-data; name=bar


    3
    BAR
    1e

    --o5bjIygPrPrCCYUp8FyfvsQe--
    0

If you want to force the rendering of an unchunked request you can call 
`toStrict` on the `formData` first.
This would create a request looking something like this:

    POST / HTTP/1.1
    Host: example.com
    User-Agent: akka-http/test
    Content-Type: multipart/form-data; boundary=1QipXPrd9L25eJwcQYgiNbfA
    Content-Length: 264

    --1QipXPrd9L25eJwcQYgiNbfA
    Content-Type: text/plain; charset=UTF-8
    Content-Disposition: form-data; name=foo

    FOO
    --1QipXPrd9L25eJwcQYgiNbfA
    Content-Type: text/plain; charset=UTF-8
    Content-Disposition: form-data; name=bar

    BAR
    --1QipXPrd9L25eJwcQYgiNbfA--

There is no www-url-encoding happening anywhere.

Cheers,
Mathias


On Tuesday, October 13, 2015 at 11:33:43 PM UTC+2, Eric Swenson wrote:
>
> 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