動機
omniauthでログイン処理を実装した際、ユーザの画像もデフォルトで保存しておきたい。
実装するのはよいが、モデルに実装してしまうと複数のモデルで使うときに、同じコードを書かないといけないので、S3への画像アクセッサとしてConcernに切り出しました。
また、rmagickを使ってリサイズできるようにもしました。
前提
rmagcikはネイティブライブラリのImageMagcikに依存するのであらかじめインストールしておく必要があります。
コード
GemFile
# ruby version
ruby '2.1.5'
# rails version
gem 'rails', '4.2.1'
gem 'rmagick'
gem 'aws-sdk', '~> 1'
gem 'aws-s3', require: 'aws/s3'
config/s3.yml
bucket: 保存したいバケット名を書いてください
access_key_id: アクセスキーを書いてください
secret_access_key: シークレットアクセスキーを書いてください
S3Access.rb
module S3Access
extend ActiveSupport::Concern
included do
require 'open-uri'
require 'rmagick'
end
# クラスメソッド定義
module ClassMethods
end
# インスタンスメソッド
# 拡張子取得
def image_types
# code
{'JPEG' => 'jpg', 'GIF' => 'gif', 'PNG' => 'png'}
end
#バケットのurlを返すメソッド
#このurlをモデル側で永続化しておき、ビューにはこのurlを表示して画像を参照する
def backet_url
# code
s3config = YAML.load_file(Rails.root.join('config', 's3.yml'))
"http://s3-us-west-2.amazonaws.com/#{s3config['bucket']}"
end
#バケット名を返すメソッド
def backet
s3config = YAML.load_file(Rails.root.join('config', 's3.yml'))
s3 = AWS::S3.new(access_key_id: s3config['access_key_id'], secret_access_key: s3config['secret_access_key'])
s3.buckets[s3config['bucket']]
end
# url経由で読み込んだ画像をs3に保存する.
# omniauth経由でのユーザ画像登録時はこれを呼び出す
# uri : "https://graph.facebook.com/527606440739083/picture?width=200&height=200"
# key s3に保存するファイルのパス
# height, width リサイズする幅、高さを指定
def save_from_uri(uri, key, width, height)
open(uri) do |data|
save_from_binary(data.read, key, width, height)
end
end
#アプリないで画像再設定する場合はこちらを呼ぶ
def update_from_binary(binary, key, width, height)
b = backet
object = b.objects["#{key}.jpg"]
object.delete if object.exists?
save_from_binary(binary, key, width, height)
end
private:
#保存処理共通メソッド
def save_from_binary(binary, key, width, height)
# code
img = Magick::Image.from_blob(binary).first.resize_to_fill(width, height)
img.format = 'JPEG'
b = backet
object = b.objects["#{key}.#{image_types[img.format]}"]
unless object.exists?
object.write(
img.to_blob, # 一つ目の引数でバイナリを渡してしまえば良い
acl: :private, # 非公開にする場合、ここで:privateを指定しておく
content_type: "image/#{img.format.downcase}",
)
end
end
end