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