業務でアイコン登録機能を追加することになり、
画像・ファイルアップロード機能はこれから先どんなシステムでも
必要とされるだろうと思い、書き残しておきます。
最近Rails5系が出てきたので、そろそろ4系の情報が必要とされなくなる?かも
使用技術
Ruby2.1.2
Rails4.2
carrierwave # 画像アップロード用
rmagick # トリミング用
fog # S3へのアップロードを簡単にしてくれるライブラリ
やったこと
ー導入編ー
今回のファイルアップロード先はAWS S3になったので、AWSの設定をします。
AWSの設定は、
- AWSアカウント作成
- IAMユーザー作成
- S3にてバケット作成
ていう流れになるかと。
詳細な手順は以下の引用元を参考にしてみてください。今回は省略します。
AWSアカウント作成
IAMユーザー作成とバケット作成
RailsとCarrierWaveでAmazon S3に画像を保存する
この段階で、AWSのアクセスキーIDとシークレットアクセスキーを取得しましょう。
次はアプリ側の設定です。Gemfileに以下を記載します。
gem 'carrierwave'
gem 'rmagick'
gem 'fog'
bundle install します。
※自分はbundle install でrmagickのインストール中に
checking for Magick-config... no
というエラーが出たので、以下で対応しました。
$ brew install imagemagick
$ gem install rmagick
$ bundle install
ここまで出来たら、Carrierwaveの設定をします。
まずは、envファイルに先ほど取得したAWSのキーとリージョン、バケット名とバケットまでの
URLを記載します。
AWS_ACCESS_KEY_ID = 先ほど取得したアクセスキー
AWS_SECRET_ACCESS_KEY = 先ほど取得したシークレットアクセスキー
AWS_REGION= ap-northeast-1 # 東京
AWS_S3_BUCKET= バケット名
AWS_S3_URL= バケットまでのURL
config配下にcarrierwave.rbを作成し、以下記載
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'AWS',
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
region: ENV['AWS_REGION'],
path_style: true
}
config.fog_public = true # public-read
config.remove_previously_stored_files_after_update = false
config.fog_directory = ENV['AWS_S3_BUCKET']
config.asset_host = ENV['AWS_S3_URL']
end
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
ここで活躍してくれるのがfogです。fogがAWSの認証をしてくれます。
今回は環境毎にenvを書き換える形でバケットを変えています。
case when
とかで環境毎にバケットを変えることもできます。
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
これは、日本語のファイル名が____に置き換わってしまうのを防ぐために記載しています。
ここまでで、設定はほぼ完了です。
ーアップロード編ー
続いて、実際にAWS S3に画像をアップロードします。
アップロードクラスを作成します。
業務ではユーザーアイコンを設定する機能だったので、iconという名前にしています。
bundle exec rails g uploader icon
これで、app/uploaders/icon_uploader.rbが作成されます。
続いてicon_uploader.rbを使いやすいように修正します。
class IconUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick # 画像トリミングしますよー
storage :fog # fog使いますよー
# cash使います。
def fog_attributes
{
'Content-Type' => 'image/jpg',
'Cache-Control' => "max-age=#{1.week.to_i}"
}
end
# バケット以下アイコンの保存先を指定します。
# ~/[バケット名]/[foldername] 配下に画像がアップロードされます。
def store_dir
"[foldername]"
end
# アップロード可能な形式をここで指定します。
# ちなみにアップロード不可な形式の指定はextension_black_list
def extension_white_list
%w(jpg jpeg png)
end
# アップロード時のファイル名を指定します。
# アップロードしたファイルを一意に認識
def filename
if original_filename.present?
"#{model.id}_#{secure_token}.#{file.extension}"
end
end
protected
def secure_token
var = :"@#{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
end
Modelのカラムにアップローダーを紐付けます。
# ↓ 以下を追加
mount_uploader :icon, IconUploader
migrationファイルに作成したアップローダーと同じ名前でカラムを追加します。
ここまで設定したら、migrate
bundle exec rake db:migrate
これで、viewにファイルアップロードを追加すれば画像がアップロードされます。
画面に出すときには以下のコードを追加すればOKです。
<%= image_tag model.icon_url %>
model.icon_urlで勝手にS3までのurlを付加してくれます。
アイコンをヘッダーとかで常に表示したい場合には、付加してくれる画面とそうでない画面
がcontrollerで分かれるので、URL設計はしないといけません。
これでRails+carrierwave+fogでs3に画像アップロードは出来ました。
ートリミング・リサイズ編ー
最後に画像のトリミング・リサイズです。
トリミング・リサイズにはRmagickを使います。
Rmagickはいろんなメソッドがあるので、自分が意図する形でトリミングしてくれるように
メソッドを使います。
リサイズするだけなら、この記事がすごくわかりやすいです。
CarrierWave + RMagick 画像のリサイズをまとめてみました
ということで、自分も要件で100x100サイズに画像をリサイズする必要があったので、
アップローダーに以下記載します。
version :thumb do
process :resize_to_limit => [100, 100]
end
これで、オリジナルとは別に100x100で画像がアップロードされるようになります。
また、resize_to_limit
は縦横比固定でアップロードしてくれます。
でもでも、このままアップロードすると、横長の画像が縦長に表示されるので、
結局トリミングしないといけませんでした。
解決策
また、トリミングの際には画像に対して中央から100x100で切り取るようにしないといけません。
まぁ、そこまで便利に作られているわけではないので、自前で拡張します。
ここの情報がすごく少なかったですね。
version :thumb do
process :crop
process :resize_to_limit => [100, 100]
end
def crop
manipulate! do |img|
gravity = Magick::CenterGravity # 中央から切り取ります。
crop_w = img.columns
crop_h = img.rows
# 画像のサイズが縦横違った場合は小さい方に合わせてトリミングする。
if img.rows <= img.columns
crop_w = img.rows
else
crop_h = img.columns
end
img.crop!(gravity, crop_w, crop_h)
img = yield(img) if block_given?
img
end
version :thumb
で、自前でトリミングをして、縦横サイズを合わせて
100x100にリサイズしています。
これで、縦横の長さが違くても綺麗に表示されるようになります。
crop
メソッドが切り取りを行います。
そこの情報は以下を参考にしました。
RubyのRMagickで縦横比固定でリサイズしたり切り抜いたり
以上で、画像のトリミング・リサイズは終わりです。
実装してみて
画像アップロードは初めてだったので、いろいろ調べたり先輩のコードを読みながら
進められたので比較的サクサクできたかなと。
ただ、情報がちらばっていたので、画像アップロードの導入からトリミング・リサイズまで
ある詳細な記事があったら便利だなと思ったので今回書いてみました。
facebookやtwitterとかもアイコン登録がありますが、アップロード時に任意の場所で
トリミングさせていますよね。
今回は自分の技術力不足によって実装期間に余裕が持てませんでしたが、余裕があれば
取り組みたいところです。