使用移动客户端的查询参数获取 S3 预签名 post url
Obtain S3 presigned post url with query parameters for a mobile client
我正在为 Rails 4 的后端服务创建一个 API。
该服务需要将图像文件上传到 amazon s3 存储桶。
我想使用直接上传 url,这样客户端就可以管理到 s3 的上传,而服务器不会一直忙。
目前我有以下原型 rails 动作
def create
filename = params[:filename]
s3_direct_post = S3_BUCKET.presigned_post(key: "offers/#{SecureRandom.uuid}/#{filename}", acl: 'public-read')
s3p = s3_direct_post.fields
url = "#{s3_direct_post.url}/#{filename}?X-Amz-Algorithm=#{s3p['x-amz-algorithm']}&X-Amz-Credential=#{s3p['x-amz-credential']}&X-Amz-Date=#{s3p['x-amz-date']}&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=#{s3p['x-amz-signature']}"
render json: {success: true, url: url}, status: :ok
end
这会生成这样一个 url:
https://my-bucket.s3.eu-central-1.amazonaws.com/test.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MYKEY/20150420/eu-central-1/s3/aws4_request&X-Amz-Date=20150420T162603Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=MYSIGNATURE
现在我尝试 post test.png 到 url 与以下内容:
curl -v -T test.png "url"
我收到以下错误响应:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>MYKEY</AWSAccessKeyId>...
我认为问题出在指定的 X-Amz-SignedHeaders Header 错误这一事实。我不确定亚马逊 rails sdk gem 默认使用哪个 headers。
我应该如何更改我的url生成,以便移动客户端可以将url和post一个文件带到它?
这是一个解决方案:
在config/initializers/aws.rb
中:
AWS_CREDS = Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
Aws.config.update({
region: 'eu-central-1',
credentials: AWS_CREDS
})
S3 = Aws::S3::Resource.new('eu-central-1')
S3_BUCKET_NAME = ENV['S3_BUCKET_NAME']
S3_BUCKET = S3.bucket(S3_BUCKET_NAME)
在你的 model/controller/concern/or 中:
obj = S3_BUCKET.object("offers/#{user.id}/#{self.id}")
url = obj.presigned_url(:put) # obj.presigned_url(:put, acl: 'public-read') #if you want to make the file public
然后上传可以使用手机客户端或者curl:
curl -X PUT -T file_to_upload "url from above"
请注意,如果您使用 public-read
acl 选项,则必须添加 x-amz-acl: public-read
header:
curl -H "x-amz-acl: public-read" -X PUT -T file_to_upload "url from above"
我正在为 Rails 4 的后端服务创建一个 API。 该服务需要将图像文件上传到 amazon s3 存储桶。
我想使用直接上传 url,这样客户端就可以管理到 s3 的上传,而服务器不会一直忙。
目前我有以下原型 rails 动作
def create
filename = params[:filename]
s3_direct_post = S3_BUCKET.presigned_post(key: "offers/#{SecureRandom.uuid}/#{filename}", acl: 'public-read')
s3p = s3_direct_post.fields
url = "#{s3_direct_post.url}/#{filename}?X-Amz-Algorithm=#{s3p['x-amz-algorithm']}&X-Amz-Credential=#{s3p['x-amz-credential']}&X-Amz-Date=#{s3p['x-amz-date']}&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=#{s3p['x-amz-signature']}"
render json: {success: true, url: url}, status: :ok
end
这会生成这样一个 url:
https://my-bucket.s3.eu-central-1.amazonaws.com/test.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MYKEY/20150420/eu-central-1/s3/aws4_request&X-Amz-Date=20150420T162603Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=MYSIGNATURE
现在我尝试 post test.png 到 url 与以下内容:
curl -v -T test.png "url"
我收到以下错误响应:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>MYKEY</AWSAccessKeyId>...
我认为问题出在指定的 X-Amz-SignedHeaders Header 错误这一事实。我不确定亚马逊 rails sdk gem 默认使用哪个 headers。
我应该如何更改我的url生成,以便移动客户端可以将url和post一个文件带到它?
这是一个解决方案:
在config/initializers/aws.rb
中:
AWS_CREDS = Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
Aws.config.update({
region: 'eu-central-1',
credentials: AWS_CREDS
})
S3 = Aws::S3::Resource.new('eu-central-1')
S3_BUCKET_NAME = ENV['S3_BUCKET_NAME']
S3_BUCKET = S3.bucket(S3_BUCKET_NAME)
在你的 model/controller/concern/or 中:
obj = S3_BUCKET.object("offers/#{user.id}/#{self.id}")
url = obj.presigned_url(:put) # obj.presigned_url(:put, acl: 'public-read') #if you want to make the file public
然后上传可以使用手机客户端或者curl:
curl -X PUT -T file_to_upload "url from above"
请注意,如果您使用 public-read
acl 选项,则必须添加 x-amz-acl: public-read
header:
curl -H "x-amz-acl: public-read" -X PUT -T file_to_upload "url from above"