awslabs / aws-sdk-kotlin

Multiplatform AWS SDK for Kotlin
Apache License 2.0
410 stars 49 forks source link

Missing Presigned POST / POST Policy support for multipart form-data file uploads #809

Open Somnium99 opened 1 year ago

Somnium99 commented 1 year ago

Describe the feature

For Pre-signed multipart form-data uploads, one must use POST (PUT is for binary data). Therefore AWS introduced the POST Policy.

Is your Feature Request related to a problem?

It's impossible to use multipart form-data uploads in this sdk.

Proposed Solution

UploadPartRequest should also include support for POST, and not do always PUT internally.

To support POST Policy, there needs to be something that:

  1. forms the policy
  2. Base64-encodes it
  3. Signs it using AWS SigV4.

Describe alternative solutions or features you've considered

No response

Acknowledge

AWS Kotlin SDK version used

v0.19.2-beta

Platform (JVM/JS/Native)

JVM

Operating System and version

MacOS

ianbotsf commented 1 year ago

Hi @Somnium99, thanks for reaching out. If you're looking to pre-sign PutObject requests, the AWS SDK for Kotlin provides an extension method on the request object:

val s3 = ... // An already-configured S3 client

val putRequest = PutObjectRequest {
    bucket = "issmith-test-bucket-us-west-2"
    key = "foo"
}
val presignedPutRequest = putReq.presign(s3.config, 1.days)

The returned object is an HttpRequest. The object contains URL necessary to execute the request with the HTTP client of your choice.

For example, to send a presigned PutObject request with OkHttp:

val client = OkHttpClient()
val textPlainMediaType = "text/plain".toMediaType()
val objectContents = "Foo text!"

val okRequest = Request
    .Builder()
    .url(presignedPutRequest.url.toString())
    .put(objectContents.toRequestBody(textPlainMediaType))
    .build()

val okResponse = client.newCall(okRequest).execute()

The POST (multipart/form-data) upload variant of PutObject is intended for use in browsers specifically. The SDK does not provide a mechanism to generate a signed POST request for browsers.

Does that answer your question?

Somnium99 commented 1 year ago

Hi @ianbotsf , thank you for the response I'm not sure I understood, allow me to explain in more detail.

I want to generate in our Server presigned urls for our clients (browsers + mobile platforms(Android + iOS)), so the clients could upload the file by parts, using form-data (POST) multipart. Like depicted here (but, for presigning each Part, and not just the one whole file)

However, The current UploadPartRequest in this SDK does it for PUT method (this means we cannot use form-data for multipart).

To support POST Policy, there needs to be something that:

  1. forms the policy
  2. Base64-encodes it
  3. Signs it using AWS SigV4.

    And of course an option to add uploadId + partNumber for presigning the parts individually , and not just one big file.

Perhaps it's not a fix, but rather a new feature to support POST Policy. Here's an example explaining the POST Policy : https://www.matano.dev/blog-archive/2022/02/14/s3-post-policy#enter-post-policy

Thank you

ianbotsf commented 1 year ago

To clarify, the GH link you posted talks about multipart S3 uploads. That is the process of completing a object upload across multiple API calls (e.g., CreateMultipartUpload, UploadPart, UploadPart, UploadPart, ..., CompleteMultipartUpload). This is unrelated to multipart-encoded forms.

The AWS SDK for Kotlin does not yet have a mechanism to create multipart-encoded requests that could be used to construct web forms for submission by a browser. We'd like to add this feature in the future but at this time you must construct the necessary form fields yourself (including signing whatever policy is attached). I'll leave this issue open for tracking and we'll update it when the work is complete.

trinopoty commented 2 days ago

I'll put this here for anyone looking for this feature: https://github.com/pschichtel/aws-s3-post-object-presigner https://central.sonatype.com/artifact/tel.schich/aws-s3-post-object-presigner