关于Amazon S3:rails,回形针,直接上载到S3-身份验证的URL返回SignatureDoesNotMatch响应

Rails, Paperclip, Direct Upload to S3 - Authenticated URL returns SignatureDoesNotMatch response

rails(?> 3.2.13)

回形针(2.7.4)

aws-sdk(1.8.0)

在使用Paperclip的Rails应用程序中,我有一个名为" Asset"的模型,该模型具有一个名为" upload"的附件(s3_permissions:" private")。

此附件首先直接上传到Amazon S3,然后保存资产。要进行直接上传,我使用了以下gem
通过遵循教程http://www.blitztheory.com/direct-upload-with-s3_direct_upload/

中的说明,https://github.com/waynehoover/s3_direct_upload gem

注意:

本教程中我忽略的一件事是我没有使用类方法copy_and_delete(paperclip_file_path,raw_source)在所需路径中重新创建存储桶中的资产。

我首先从视图中将图像" Mars.gif"上传到Amazon S3,并将其与我的Asset对象关联。该图像已成功上传到存储桶中,资产也已成功保存。我正在查看其详细信息,并尝试使用图像的源URL打开附件" Mars.gif":

http://s3.amazonaws.com/mm_tom_test/uploads/1390421005820-yff5taw7kgf-da8b488d45d747deb206977d29e11005/Mars.gif?AWSAccessKeyId=AKIAIMKHM4EATOHUAQ3Q


我自己解决了这个问题,并回答了我自己的问题,以防其他人最终遇到上述问题。

S3DirectUpload gem在其uploadComplete回调中接收Amazon S3存储桶对象的键作为编码值,并以正斜杠" /"作为前缀。 /uploads%2F1390554749261-nuflsns5cn-0de4e0f6e495e02bc5ee0c853d56b95f%2Fflower-3.jpeg

如果将该键保存为应用程序的数据库中的相同键,然后使用upload.expiring_url(对于名为upload的附件)访问生成的到期URL,则显示SignatureDoesNotMatch
从Amazon S3返回。

但是,如果我们先清理最初收到的密钥,则可以通过以下方式将其保存到应用程序的数据库中:

  • 解码密钥中的编码值
  • 从密钥中删除前缀正斜杠(" / ")
  • 然后在访问时生成的到期URL允许成功访问资源。

    万一有人遇到这个问题,我在这里给出我模型的工作代码片段。其他代码片段可以在我上面的评论中找到。

    /app/models/asset.rb

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
      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,其中确切提到了我所面临的问题。

    当我使用回形针(2.7.4)时,其S3存储区不包含在拉取请求中所做的更改。我通过检查rvm目录下本地安装的gem中的源代码进行了验证。因此,我更改了上述代码具有以下内容:

    /config/initializers/paperclip.rb

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    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

    谢谢,

    吉涅什