paperclipをamazon s3@tokyoで使う


嵌ったのでメモ。

paperclipの使い方

これは簡単です。本当ありがたいです。
thoughtbot/paperclip - GitHub


s3で使う

これも割と簡単です。
modelでstorageにs3、s3_credentialsをymlに定義して読み込ませます。

PaperClipを使って本番の時だけAmazon S3を使う - func09


tokyo regionで使う

こんなエラーになります
AWS::S3::PermanentRedirect in UsersController#create

The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.


これは、aws-s3のconstを変更することで解決です。

#config/initializers/aws.rb
AWS::S3::DEFAULT_HOST.replace "s3-ap-northeast-1.amazonaws.com"


しかし表示する際のurlがs3.amazonaws.comになってしまってます。
http://s3.amazonaws.com/bucket/avatars/3/original.jpeg?1302193703


ソースを見てみると

こんな感じです。
#paperclit/attachment.rb

module Paperclip
  class Attachment

    def url(style_name = default_style, use_timestamp = @use_timestamp)
      url = original_filename.nil? ? interpolate(@default_url, style_name) : interpolate(@url, style_name)
      use_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
    end
    def original_filename
      instance_read(:file_name)
    end
    def instance_read(attr)
      getter = :"#{name}_#{attr}"
      responds = instance.respond_to?(getter)
      cached = self.instance_variable_get("@_#{getter}")
      return cached if cached
      instance.send(getter) if responds || attr.to_s == "file_name"
    end
@urlが怪しそうです
#paperclip/storage/s3.rb
module Paperclip
  module Storage

    module S3
      def self.extended base
        begin
          require 'aws/s3'
        rescue LoadError => e
          e.message << " (You may need to install the aws-s3 gem)"
          raise e
        end unless defined?(AWS::S3)

        base.instance_eval do
          @s3_credentials = parse_credentials(@options[:s3_credentials])
          @bucket         = @options[:bucket]         || @s3_credentials[:bucket]
          @bucket         = @bucket.call(self) if @bucket.is_a?(Proc)
          @s3_options     = @options[:s3_options]     || {}
          @s3_permissions = @options[:s3_permissions] || :public_read
          @s3_protocol    = @options[:s3_protocol]    || (@s3_permissions == :public_read ? 'http' : 'https')
          @s3_headers     = @options[:s3_headers]     || {}
          @s3_host_alias  = @options[:s3_host_alias]
          unless @url.to_s.match(/^:s3.*url$/)
            @path          = @path.gsub(/:url/, @url)
            @url           = ":s3_path_url"
          end
          AWS::S3::Base.establish_connection!( @s3_options.merge(
            :access_key_id => @s3_credentials[:access_key_id],
            :secret_access_key => @s3_credentials[:secret_access_key]
          ))
        end
        Paperclip.interpolates(:s3_alias_url) do |attachment, style|
          "#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
        end unless Paperclip::Interpolations.respond_to? :s3_alias_url
        Paperclip.interpolates(:s3_path_url) do |attachment, style|
          "#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
        end unless Paperclip::Interpolations.respond_to? :s3_path_url
        Paperclip.interpolates(:s3_domain_url) do |attachment, style|
          "#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
        end unless Paperclip::Interpolations.respond_to? :s3_domain_url
      end
最後の3ブロックで

urlを生成しているのが分かります。
デフォルトは、:s3_path_urlで2つめのブロックです。
なんと「s3.amazonaws.com」直書きですね。


なので

モンキーパッチ的にここを書き換えるか、:s3_alias_urlを使えばなんとかできそうです。
後者のほうでやってみた結果が下記です。
#app/models/user.rb

class User < ActiveRecord::Base
  has_attached_file :avatar,
    :storage => :s3,
    :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
    :path => ":attachment/:id/:style.:extension",
    :bucket => 'mybucket',
    :url => ":s3_alias_url",
    :s3_host_alias => "s3-ap-northeast-1.amazonaws.com/mybucket"
end
表示もできました。パチパチ。

でもなんだかなぁ。