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

Shunsuke Sawada

Screen Shot 2013-10-01 at 6.07.43 PM

サクッと導入できるので嬉しい。

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
9
Shunsuke Sawada

おすすめの記事

CakePHP 2.x JSヘルパーでajax通信(ajax helperは使わない)
20
Rails4でQiita投稿ボタンをつくった
18
紙のデザイナーがウェブ開発できるようになるまでに必要なこと
451