We stumbled upon the same issue today and were trying to use
// does not work
request.putCustomRequestHeader(Headers.S3_USER_METADATA_PREFIX + "foo", "bar");
which unfortunately does not really work, it adds the metadata but the caller of the presigned url has to still provide the metadata using request headers which is something the client should not have to do.
Finally we found that using GeneratePresignedUrlRequest#addRequestParameter
does the job marvellously:
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest("bucket", "yourFile.ending");
request.addRequestParameter(Headers.S3_USER_METADATA_PREFIX + "foo", "bar");
// import com.amazonaws.services.s3.Headers; needed
The presigned url then looks something like
https://bucket.s3.region.amazonaws.com/yourFile.ending?x-amz-meta-foo=bar&X-Amz-Security-Token=...
The metadata can be clearly seen in the url, using Postman to PUT
to that file using upload-file
in the body creates the file with the correct metadata in the bucket and it is not possible for the client to change the meta-data because that would make the signature no longer match the request.
The only not-so-pretty part about this is having to specify the internal aws header prefix for user metadata.