4

In Python boto3 library, there is a method called generate_presigned_post that let create a pre-signed URL for uploading file using POST. I do not see an equivalent in Java as the things available ("GeneratePresignedUrlRequest") only support: GET, PUT, DELETE, HEAD.

Wondering if I'm missing something, or if people can think of a way to maybe just do it by writting my own generatePresignedPost in Java?

I'm building a service in Java where I want to return a Pre-signed URl allowing to upload multiple files to a specific bucket/key prefix in S3.

I did some prototyping in Python first to understand more and got to this code:

bucket_name = "my-bucket"
key = "test/${filename}"
expires_in = 24*60*60 # 1 day

s3_client = # Initialize Boto S3 Client with Credentials

response = s3_client.generate_presigned_post(bucket_name, key, ExpiresIn=expires_in)

print(response['url'])
print(response['fields'])

(I know that "${filename}" get converted to a "start-width" condition)

The Java version of GeneratePresignedUrlRequest only allows to create PUT pre-signed URL, doesn't let me set "condition" like the POST version. This is why I'm looking to get a generate_presigned_post equivalent in Java.

The Python code end up creating a request that look like:

fields = {'key': 'test/${filename}'}
conditions = [{'bucket': 'my-bucket'}, ['starts-with', '$key', 'test/']]
request_dict = {'url_path': '/my-bucket', 'query_string': {}, 'method': 'PUT', 'headers': {}, 'body': b'', 'url': 'https://s3.us-west-2.amazonaws.com/my-bucket', 'context': {'is_presign_request': True, 'use_global_endpoint': True}}

And what's returned look like:

response['url'] = 'https://my-bucket.s3.amazonaws.com/'
response['fields'] = {'key': 'test/${filename}', 'AWSAccessKeyId': 'ACCESS_KEY', 'x-amz-security-token': 'SECURITY_TOKEN', 'policy': 'VERY_LONG_POLICY', 'signature': 'SIGNATURE'}
Arafat Nalkhande
  • 11,078
  • 9
  • 39
  • 63

2 Answers2

1

You can use the following code snippet

GeneratePresignedUrlRequest generatePresignedUrlRequest = 
                new GeneratePresignedUrlRequest(bucketName, objectKey)
                .withMethod(HttpMethod.POST)
                .withExpiration(expiration);
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

You can check here for full Java Source

Arafat Nalkhande
  • 11,078
  • 9
  • 39
  • 63
  • 1
    This doesn't result in the same behavior as the Python Code. The resulting fields are not the same. See the documentation : https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/GeneratePresignedUrlRequest.html#setMethod-com.amazonaws.HttpMethod- . POST is not one of the allowed fields – Denis Bellavance Jun 24 '19 at 17:37
  • 1
    The other feature missing from this is setting "conditions" like presented in my original question – Denis Bellavance Jun 24 '19 at 17:50
1

Found this articles from Amazon: https://aws.amazon.com/articles/browser-uploads-to-s3-using-html-post-forms/

This way works to generate the signature:

import sun.misc.BASE64Encoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

String policy = (new BASE64Encoder()).encode(
    policy_document.getBytes("UTF-8")).replaceAll("\n","").replaceAll("\r","");

Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec(
    aws_secret_key.getBytes("UTF-8"), "HmacSHA1"));
String signature = (new BASE64Encoder()).encode(
    hmac.doFinal(policy.getBytes("UTF-8")))
    .replaceAll("\n", "");

Would still have been much easier if this was readily available from the AmazonS3Client.