rails(〜> 3.2.13)
回形针(2.7.4)
aws-sdk(1.8.0)
在使用Paperclip的Rails应用程序中,我有一个名为“Asset”的模型,其附件名为“upload”(s3_permissions:“private”)。
此附件首先直接上传到Amazon S3,然后保存资产。为了直接上传,我使用了以下gem 按照教程https://github.com/waynehoover/s3_direct_upload
中的说明操作http://www.blitztheory.com/direct-upload-with-s3_direct_upload/ gem注意:
我从教程中忽略的一件事是我没有使用类方法copy_and_delete(paperclip_file_path,raw_source)在所需路径中重新创建存储桶中的资产。
我首先将视图“Mars.gif”从我的视图上传到Amazon S3并将其与我的Asset对象关联。图像已成功上传到存储桶,资产也已成功保存。但是在保存资产时我正在查看其详细信息,并尝试使用图像的源URL打开附加的“Mars.gif”:
我遇到 SignatureDoesNotMatch 错误。请在下面找到Amazon S3返回的XML:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<StringToSignBytes>47 45 54 0a 0a 0a 31 33 39 30 34 33 33 39 36 33 0a 2f 6d 6d 5f 74 6f 6d 5f 74 65 73 74 2f 75 70 6c 6f 61 64 73 25 32 46 31 33 39 30 34 32 31 30 30 35 38 32 30 2d 79 66 66 35 74 61 77 37 6b 67 66 2d 64 61 38 62 34 38 38 64 34 35 64 37 34 37 64 65 62 32 30 36 39 37 37 64 32 39 65 31 31 30 30 35 25 32 46 4d 61 72 73 2e 67 69 66 3f 72 65 73 70 6f 6e 73 65 2d 63 6f 6e 74 65 6e 74 2d 64 69 73 70 6f 73 69 74 69 6f 6e 3d 69 6e 6c 69 6e 65 26 72 65 73 70 6f 6e 73 65 2d 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3d 69 6d 61 67 65 2f 67 69 66</StringToSignBytes>
<RequestId>9E297018ADD4D5AD</RequestId>
<HostId>EtMgiHpNfywzw7cNxAoCBFW5fY80LY3E5nTUuP182NfjzYqFTizIgjS+bgqPKM33</HostId>
<SignatureProvided>ehSbrI2bKE4jqQNHyPJKWDySMyU=</SignatureProvided>
<StringToSign>GET
1390433963
/mm_tom_test/uploads%2F1390421005820-yff5taw7kgf-da8b488d45d747deb206977d29e11005%2FMars.gif?response-content-disposition=inline&response-content-type=image/gif</StringToSign>
<AWSAccessKeyId>AKIAIMKHM4EATOHUAQ3Q</AWSAccessKeyId>
</Error>
我已经花了足够的时间来解决这个问题而没有成功。如果社区中的任何人能够帮助我弄清楚导致此签名不匹配问题的原因以及如何解决这个问题,我真的很感激吗?
在我使用的代码下面列出:
/config/initializers/paperclip.rb
Paperclip::ASSET_EXPIRATION_TIME = 3600.seconds
Paperclip.interpolates(:key) do |attachment, style|
attachment.instance.key
end
Paperclip.interpolates(:s3_conditional_url) do |attachment, style|
attachment.expiring_url(Paperclip::ASSET_EXPIRATION_TIME, style)
end
module Paperclip::Storage::S3
def public_url(style_name = default_style)
if path
"http://#{s3_host_name}/#{bucket_name}/#{path(style_name)}"
end
end
def expiring_url(time = Paperclip::ASSET_EXPIRATION_TIME, style_name = default_style)
if path
s3_object(style_name).url_for(:read, :expires => time, :secure => use_secure_protocol?(style_name), :response_content_disposition => "inline",
end
end
end
/config/amazon_s3.yml
development:
access_key_id: 'AKIAIMKHM4EATOHUAQ3Q'
secret_access_key: '<SECRET ACCESS KEY>'
bucket: 'mm_tom_test'
/lib/s3_decider.rb
module S3Decider
def self.included(model)
model.class_eval do
if Paperclip::FILESYSTEM_ENVS.include?(Rails.env)
@s3_decider = {
:path => ":rails_root/public/assets/:class/:attachment/:id/:style/:basename.:extension",
:url => "/assets/:class/:attachment/:id/:style/:basename.:extension"
}
else
@s3_decider = {
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/amazon_s3.yml",
:s3_permissions => "private",
:s3_protocol => Rails.env == "development" ? "http" : "https",
:path => ":class/:attachment/:id/:style.:extension",
:url => ":s3_conditional_url"
}
end
end
end
end
/app/models/asset.rb
class Asset < ActiveRecord::Base
include S3Decider
attr_accessible :upload, :upload_file_name, :upload_content_type, upload_file_size, :upload_updated_at
paperclip_options_for_upload = @s3_decider.merge(
path: ":key"
)
has_attached_file :upload, paperclip_options_for_upload
end
感谢。
答案 0 :(得分:2)
我自己解决了这个问题并回答了我自己的问题,以防其他人最终解决上述问题。
uploadComplete
回调中的 S3DirectUpload gem将Amazon S3存储桶对象的密钥作为编码值接收,并以正斜杠为前缀“/”。 /uploads%2F1390554749261-nuflsns5cn-0de4e0f6e495e02bc5ee0c853d56b95f%2Fflower-3.jpeg
如果保存在应用程序的数据库中,然后使用upload.expiring_url
(对于名为upload
的附件)访问生成的过期URL,则显示 SignatureDoesNotMatch
从Amazon S3返回。
但是,如果我们在保存到应用程序的数据库之前清理最初收到的密钥:
然后访问时生成的到期URL允许成功访问资源。
如果有人遇到此问题,我将在此处提供该模型的工作代码段。其他代码段可以在我的上述评论中找到。
<强> /app/models/asset.rb 强>
class Asset < ActiveRecord::Base
include S3Decider
attr_accessible :upload, :upload_file_name, :upload_content_type, upload_file_size, :upload_updated_at
attr_accessible :key
paperclip_options_for_upload = paperclip_options_for_upload.merge(
path: ":key",
url: ":s3_conditional_url",
s3_url_options: lambda { |model|
{
response_content_type: model.upload_content_type,
response_content_disposition: "inline"
}
}
)
has_attached_file :upload, paperclip_options_for_upload
def key=(key)
return if key.blank?
# S3DirectUpload receives the Amazon S3 bucket object's key
# as encoded and prefixed by a forward slash.For e.g.
# /uploads%2F1390554749261-nuflsns5cn-0de4e0f6e495e02bc5ee0c853d56b95f%2Fflower-3.jpeg
# Sanitizing it here else programmatically accessing the bucket object
# corresponding to the key prefixed with "/" shall throw a No such key
# exception.
sanitized_key = key.sub(%r{^/},'')
decoded_key = CGI.unescape(sanitized_key)
write_attribute(:key, decoded_key)
end
end
在搜索解决方案时,我遇到了拉取请求#769,它提到了我正面临的问题。
当我使用paperclip(2.7.4)时,它的S3存储不包含在pull请求中完成的更改。我通过检查rvm目录下本地安装的gem中的源代码来验证。因此我更改了上面提到的代码以下内容:
<强> /config/initializers/paperclip.rb 强>
Paperclip::ASSET_EXPIRATION_TIME = 3600.seconds
Paperclip.interpolates(:key) do |attachment, style|
attachment.instance.key
end
Paperclip.interpolates(:s3_conditional_url) do |attachment, style|
attachment.expiring_url(Paperclip::ASSET_EXPIRATION_TIME, style)
end
module Paperclip::Storage::S3
def public_url(style_name = default_style)
if path
"http://#{s3_host_name}/#{bucket_name}/#{path(style_name)}"
end
end
def expiring_url(time = Paperclip::ASSET_EXPIRATION_TIME, style_name = default_style)
if path
# Reference: https://github.com/thoughtbot/paperclip/pull/769
base_options = { :expires => time, :secure => use_secure_protocol?(style_name) }
s3_object(style_name).url_for(:read, base_options.merge(s3_url_options)).to_s
end
end
# Reference: https://github.com/thoughtbot/paperclip/pull/769
def s3_url_options
s3_url_options = @options[:s3_url_options] || {}
s3_url_options = s3_url_options.call(instance) if s3_url_options.is_a?(Proc)
s3_url_options
end
end
谢谢,
Jignesh