Presigned URLs with ExAws

Hello! I am using ExAws and the code below to make a presigned request for uploading image files to Amazon S3. All is well except that the permissions on the uploaded file are not being set to public-read:

 def make_presigned_url(bucket, path, mime_type) do
    query_params = [{"ContentType", mime_type}, {"ACL", "public-read"}]
    presign_options = [query_params: query_params]
    {:ok, presigned_url} = ExAws.Config.new(:s3) 
        |> ExAws.S3.presigned_url(:put, bucket, path, presign_options)
    presigned_url 
 end

Do you see any other mistakes?

PS. Here is .typical presigned url produced with the above:

https://s3.amazonaws.com/noteimages/jxxcarlson/carr_fire_2.jpg?ACL=public-read&ContentType=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJQYJYCIAWH6DGHIQ%2F20180809%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20180809T045031Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=e6e620f563606cfea26340c0f8f402bad35a5c50a841b25d5168bccf52f6da15
1 Like

I think this might simply not be possible. If you take a look at the AWS Java SDK docs, you can see that only a handful of parameters that would usually be sent as headers are supported (there is setContentMd5, setContentType, etc - but not setACL).
Also check out this GitHub issue with two possible workarounds.

1 Like

Thanks, it indeed seems to be the case that one cannot set the ACL this way. Do you know if it i possible to set the bucket parameters so that uploaded files are automatically publicly readable?

What i am trying to accomplish is to allow users of my app to upload images that will be publicly readable.

You can set permissions for the entire bucket.

3 Likes

I got it to work, it was very simple in the end!
here’s the code

ExAws.Config.new(:s3)
|> ExAws.S3.presigned_url(
  :get,
  bucked_name,
  filename
)
8 Likes

This is really cool - so you don’t actually need to make a single thing public and I can get MUX.com to read a signed URL when making video for me. Will try this method, thanks!

Change your query_params to this:

query_params = [
      {"ContentType", type},
      {"x-amz-acl", "public-read"}
 ]
3 Likes

This Works For Me, thanks.

Thank you good sir.

I’ve been trying to set the "Content-Disposition" key to attachment via the query_params for the presigned_url in this scenario, but it doesn’t seem to be “taking”. Is there any way to do this here, that is via the ex_aws_s3 upload?

        query_params = [
        ....
          # so the browser will download this as a file
          {"ContentDisposition", "attachment"},
        ....
        ]

I managed to generate a S3 presigned download URL which behaves like a file download by setting the response-content-disposition: "attachment" header:

config = ExAws.Config.new(:s3, region: get_region())
opts = [query_params: [{"response-content-disposition", "attachment; filename=#{file.filename}"}]]

{:ok, url} = ExAws.S3.presigned_url(config, :get, get_bucket(), file_path, opts)

If you redirect the user to this URL in your controller, it will automatically download and rename the file to file.filename.

1 Like

Thanks for the tip!