简体   繁体   中英

How to do a Pre-signed POST file upload to AWS S3 in Ruby?

Related to my question about the same thing in Go .

I would like to do a pre-signed POST file upload to a bucket on AWS S3 which only has public-read with the following bucket policy:

{
    "Version": "2012-10-17",
    "Id": "Policy1441191234567",
    "Statement": [
        {
            "Sid": "Stmt1441195123456",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::mytestbucket/*"
        }
    ]
}

A pre-signed URL is created that allows anyone that has it to do an upload with HTTP POST like described here .

I have successfully got the pre-signed PUT to work that is described here . Ie I have proper credentials in ~/aws/credentials that has full access to the bucket.

In the AWS Ruby SDK , I have found that there's a PresignedPost for the bucket so I have tried the following:

require 'aws-sdk-resources'
require 'net/http'
require 'time'
require 'uri'

s3 = Aws::S3::Resource.new(region:'eu-central-1')

bucket = s3.bucket('mytestbucket')

post = bucket.presigned_post({
    key: 'larry',
    acl: "public-read",
    expires: Time.now() + 30,
    content_length_range: 1...1024,
    success_action_redirect: "https://example.com/callback",
})

puts post.url
puts post.fields

uri = URI(post.url)
fields = post.fields.merge(file: "ken sent me")
res = Net::HTTP.post_form(uri, fields)
puts res.body

Unfortunately, running this results in an error:

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>InvalidArgument</Code><Message>Conflicting query string parameters: acl, policy</Message><ArgumentName>ResourceType</ArgumentName><ArgumentValue>acl</ArgumentValue><RequestId>6132C47A14212345</RequestId><HostId>abcdKciFUKxvC4717Zm9w2ZB5lXJna+NSkxXzkb9123tjHZHb60JJa123KctSu862gY/j+a5+3w=</HostId></Error>

I have tried removing the acl field, but that results in another error:

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>MethodNotAllowed</Code><Message>The specified method is not allowed against this resource.</Message><Method>POST</Method><ResourceType>BUCKETPOLICY</ResourceType><RequestId>9B3D7AAAE45BB47F</RequestId><HostId>yk823Z12345uucETlpQaG1234T0lxqjGAX4Uka123LQ6Pf22NVf45xxMmZAlFoQHaP+C4N60oLI=</HostId></Error>

The URI is: https://mytestbucket.s3.eu-central-1.amazonaws.com

What is the problem and how can I make it work?

Thanks for any help!

UPDATE

As one of the errors say, the issue is likely a conflicting acl and bucket policy. I would like it to be read for all and upload is only possible with a pre-signed URL (where I assume the owner becomes the one who created the URL). This is how I thought I had it set up.

I think that there are multiple plolicies in play here. The bucket policy and the post policy . See also the Ruby docs on PresignedPost -- you'll have to search for "Post Policy" as there is no anchor to the heading that I can see. It looks like Ruby creates a post policy for you, but that:

you must specify every form field name that will be posted by the browser. If you omit a form field sent by the browser, Amazon S3 will reject the request.

Additionally, it appear that this PresignedPost which is provided is meant to generate an HTML form which in turn generates the POST request and URL. You could generate that instead of letting the form do it. However if you already know all the details of the URL and request -- as asked in your Go question about the same thing -- why not Upload an Object Using a Pre-Signed URL .

So I have updated the Go question with an answer and the same applies for Ruby. The problem I had was to correctly generate the multipart form data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM