画像のアップロードにcarrierwaveを使ってみたのでメモがてらまとめます。 ほかにも、比較的実装が容易なpaperclipやcarrierwaveの後継であるrefileも使ってみましたが、保存先のディレクトリ指定、ファイル名変更、確認画面作成などの要件が満たせなかったので断念しました。
gemのインストール
Gemfileに必要なgemを記述してbundle install
でインストールします。
# Gemfile gem 'carrierwave',github: 'carrierwaveuploader/carrierwave' gem "fog" gem 'rmagick'
サーバー複数台構成でELB使ってる場合はキャッシュはローカルではなくS3にしましょう。例えば、確認画面に遷移したときに違うサーバーを見に行って該当ファイルがない!エラー!みたいな不具合が防げます。
キャッシュのS3対応をする場合、carrierwave
はgithubから取得する必要があります。
また、fog
はS3へのアップロード、rmagick
はサイズなどの画像情報取得に使います。
アップローダーの作成
アップローダーを作成します。
rails g uploader image # => app/uploaders/image_uploader.rb
アップローダーのマウントとinitializerの設定
今回はUser
モデルのavaterプロパティにマウントしてみます。
独自にfile_sizeのバリデーションも追加してます。
# app/models/user.rb class User < ActiveRecord::Base mount_uploader :avater, ImageUploader validate :file_size # 5MB以上のファイルはUPLOADできないようにしてみる def file_size upload_limit = 5.megabytes.to_i if photo.file.size > upload_limit errors.add(:avater, "のサイズが大きすぎます。") end end end
# /config/initializers/carrierwave.rb CarrierWave.configure do |config| config.fog_credentials = { provider: 'AWS', aws_access_key_id: ****, aws_secret_access_key: ****, region: 'ap-northeast-1', # Tokyoの場合 } config.cache_storage = :fog # キャッシュにS3を指定 # テストとかで同じとこにUPLOADされたくないのでバケットを分けます case Rails.env when 'production' config.fog_directory = 'bucket_production' when 'staging' config.fog_directory = 'bucket_staging' when 'development' config.fog_directory = 'bucket_development' when 'test' config.fog_directory = 'bucket_test' end end
アップローダーをいろいろカスタマイズ
# app/uploaders/image_uploader.rb class ImageUploader < CarrierWave::Uploader::Base # 画像のサイズとか取得するためにRMagickをinclude include CarrierWave::RMagick # before_createみたいなもの process :store_dimensions, :store_extension # ストレージにS3を指定 storage :fog # 画像ごとに保存するディレクトリを変えたいのでオーバーライド def store_dir # 例えばidごとにディレクトリを分けてみる "avater/#{model.id}" end # ファイル名を書き換える def filename # 例えば avater_1.jpg みたいなファイル名にしてみる "avater_#{model.id}.#{file.extension}" if original_filename end # キャッシュ先のディレクトリを指定 def cache_dir "cache" end # RMagickを使って画像の幅、高さを取得する def store_dimensions if file && model img = ::Magick::Image::read(file.file).first model.width = img.columns model.height = img.rows end end # 画像の拡張子を取得する def store_extension if file && model model.extension = file.extension end end end
画像名に日本語が使えるようにする
デフォルトだとファイル名に日本語が使われている場合filename
が"____"に変換されてしまうのでinitializerに一行追加する。
# /config/initializers/carrierwave.rb CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
確認画面をつくってみる
なにかとめんどくさい確認画面をつくります。(確認画面って日本だけの文化?海外の記事でもなかなか情報がなくて苦戦しました。。)
キャッシュをS3にしている場合、インスタンスをnewしてファイルをアタッチした時点でキャッシュに画像が保存されています。
***_cache
を使うことで再アップロードしなくてもファイルの情報を使いまわすことができます。これをファイルのバリデーションエラーや確認画面に利用します。
モデル.save
した時点で、画像がcache_dirから削除されstore_dirに保存されます。
すべて書くのが面倒なので一部だけ書きます。
# app/views/hoge/confirm.html.slim ... = form_for @user, url: "/path/to/create_user" do |f| = f.hidden_field avater_cache = image_tag @user.avater.url = f.submit "アップロード" ...
# app/controllers/hoge_controller.rb ... def confirm @user = User.new(user_params) end def create_user user = User.create(user_params) end private def user_params params.permit( ... :avater, :avater_cache ... ) end ...
carrierwaveを使ってみて
今回、画像アップロード機能を実装するにあたり、paperclip→refile→carrierwaveの順で試してみました。
paperclipは実装がかなりカンタンでしたが、確認画面をつくるところで自前実装が必要だとわかり断念しました。。refileに関してはファイルがすべてprivateでしかUPLOADできなかったり?、まだまだ情報が少なかったので諦めました。笑
それに比べてcarrierwaveは自由度が高く、かつ情報を集めやすくて、とっても素敵なライブラリでした。
次回以降もお世話になろうと思います。
参考
- Ruby - CarrierWaveを使ってファイルアップロード - Qiita
- CarrierwaveでS3にアップロードさせるとき、キャッシュもS3に置く - Qiita
- rails+carrierwave+cloudFront+s3+画像サムネイル生成(nginx)な話 | ITANDI技術ブログ
- CarrierWaveでUPDATE時に前回アップロードした画像を消さないようにする。 - Qiita
- file_column から CarrierWave へ (基本編) - 篳篥日記
- Paperclip と CarrierWave を結構マジメに比較してみた - 彼女からは、おいちゃんと呼ばれています
- carrierwaveuploader/carrierwave
- thoughtbot/paperclip
- refile/refile