Rails 超お手軽な画像アップローダー CarrierWave の使い方


サクッと導入できるので嬉しい。
Ruby 2.0
Rails 4
CentOS 6.4
公式サイト;
carrierwaveuploader/carrierwave · GitHub
インストール
rmagickは画像をリサイズしたりするのに必要です。
Gemfile
1
2
gem 'carrierwave'
gem 'rmagick'
Terminal
1
$ bundle install
ImageMagickをイントールしていないのなら、エラーが出るはず。
CentOSにImageMagickをインストールするには下記。
Terminal
1
2
yum -y install libjpeg-devel libpng-devel
yum -y install ImageMagick ImageMagick-devel
下準備
Userモデルがあって、userの画像を保存できるようにすると想定。
アップローダーを作成
Terminal
1
2
$ rails g uploader image
     app/uploaders/image_uploader.rb が生成される
マイグレーションファイルを作成
(Usersテーブルにimageカラムを追加する)
Terminal
1
2
$ rails g migration add_image_to_users image:string
   db/migrate/20130930182035_add_image_to_users.rb が生成される
マイグレーション
Terminal
1
$ rake db:migrate
Userモデルを編集
models/user.rb
usersテーブルに追加したカラムの名前をmount_uploaderに指定する。
1
2
3
class User < ActiveRecord::Base
    # 下記を追加
    mount_uploader :image, ImageUploader
UsersControllerを編集
Rails4の場合は strong params を指定する必要がある。
※ Rails4以前は attr_accessible を設定する。
controllers/users_controller.rb
1
2
3
4
 private
  def user_params
    params.require(:user).permit(:name, :description, :image)
  end
アップローダーの設定を変更
app/uploaders/image_uploader.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
34
35
36
37
# encoding: utf-8
class ImageUploader < CarrierWave::Uploader::Base  
 # リサイズしたり画像形式を変更するのに必要
  include CarrierWave::RMagick
 # 画像の上限を700pxにする
  process :resize_to_limit => [700, 700]
  # 保存形式をJPGにする
  process :convert => 'jpg'
  # サムネイルを生成する設定
  version :thumb do
    process :resize_to_limit => [300, 300]
  end
  # jpg,jpeg,gif,pngしか受け付けない
  def extension_white_list
    %w(jpg jpeg gif png)
  end
 # 拡張子が同じでないとGIFをJPGとかにコンバートできないので、ファイル名を変更
  def filename
    super.chomp(File.extname(super)) + '.jpg' if original_filename.present?
  end
 # ファイル名は日本語が入ってくると嫌なので、下記のようにしてみてもいい。
 # 日付(20131001.jpgみたいなファイル名)で保存する
  def filename
    time = Time.now
    name = time.strftime('%Y%m%d%H%M%S') + '.jpg'
    name.downcase
  end
end
リサイズやコンバートはRMagickを使っている。
詳細は http://rubydoc.info/gems/carrierwave でRMagickと検索すればよい。
ビューを編集する
views/users/new.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<%= form_for(@user) do |f| %>
  <%= f.label :name %>
  <%= f.file_field :name %>
  <%= f.label :description %>
  <%= f.file_field :description %>
  <%= f.label :image %>
  <%= f.file_field :image %> (画像用に追加したカラムの名前)
<% end %>
form_forを使っている場合は自動的にやってくれるが、
form_tagを使う場合は下記のようにオプションが必要なので注意。
1
<%= form_for @user, :html => {:multipart => true} do |f| %>
画像を表示する
views/users/show.html.erb
1
2
3
4
5
 <% if @user.image? %>
  <%= image_tag @user.image.thumb.url %>
 <% else %>
  <%= image_tag 'noimage.gif' %>
 <% end %>
@user.image.thumb.urlで自動的にサムネイルのパスを出力してくれる。
元画像は@user.image.urlでオーケー。
ナイスな機能
入力フォームなんかだと、バリデーションに引っかかって、せっかくアップロードした画像が消えてる!なんてことが多い。他の入力項目のバリデーションに引っかかっただけなのに、アップロードした画像まで消えてほしくない。
そんな時のためにimage_cacheが使える。
ビューを修正するだけ。
views/users/new.html.erb
1
2
3
4
 <%= f.label :image %>
 <%= image_tag @user.image.thumb.url if @user.image? %>
 <%= f.file_field :image %>
 <%= f.hidden_field :image_cache %>
f.hidden_field :image_cache とするだけで大丈夫。
それだけだと、ユーザーが画像が残っていることに気づかないので、
@user.image.thumb.url として、フォームにサムネを表示させておいてあげると親切。
あ、strong params(Rails4以前ならattr_accessible)に image_cache を追加する必要あり。
以上。
CarrierWave は素敵だよーという記事。
追記:
RSpec & Capybara でテストする時のメモ。
テストの記述の例
1
2
3
4
5
6
7
8
9
10
 describe "with valid info" do
  before do
   fill_in "Name", with: "Your name"
   fill_in "Description", with: "Hello"
   attach_file "Image", "#{Rails.root}/app/assets/images/test_img.gif"
  end
  it "should create user" do
   expect { click_button "Send" }.to change(User, :count).by(1)
  end
 end
FactoryGirlの記述
1
2
3
4
5
6
 factory :user do
  name "Your name"
  description "Hello"
  # 指定する画像パスはお好みで
  image File.open(File.join(Rails.root, 'app/assets/images/test_image.gif'))
 end
テストでアップされる画像は別のディレクトリに保存する。
app/uploaders/image_uploader.rb
1
2
3
4
5
6
7
8
9
  # Override the directory for testing 
  if Rails.env.test?
    def cache_dir
      "#{Rails.root}/spec/support/uploads/tmp"
    end 
    def store_dir
      "#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
  end
テストでアップされた画像は毎回消す。
1
2
3
4
5
6
    # Delete all photos after testing
    config.after(:all) do
      if Rails.env.test? 
        FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads"])
      end 
    end
