Railsの画像アップロードを倍速にする方法
ユーザーに画像をアップロードしてもらえるようなウェブサービスをつくる場合、
画像アップロード機能の実装はCarrierwaveでかなり簡単に実現できます。
Carrierwaveの基本的な使い方はこちら。
Rails 超お手軽な画像アップローダー CarrierWave の使い方 | Workabroad.jp
ただ、たとえば10枚まで画像をアップできますよ、とした場合、
1つのフォームで10枚画像を添付して「送信」なんてやると、
送信完了までとても時間がかかります。
スマートフォンの画像解像度も昔にくらべたらかなり大きいし。。
ユーザーが送信完了まで待ってくれる気がしない。。。
といった時に助けてくれるGemがcarrierwave_backgrounder。
lardawge/carrierwave_backgrounder · GitHub
まぁ倍速にするというか、
画像のアップロードをバックグランドで処理させるということなので、
画像アップロード自体が倍速になるわけではないですが。すみません。
ただ、アップロード作業なしに次の画面に遷移するので、
体感的には倍速どころかもっと早くなるはず。
同じようなGemにGarrierwave_directというものがありますが、
使ってみた結果、個人的にこちらがお気に入りです。
環境
- CentOS 6.4
- Ruby 2.0
- Rails 4.0
Gem インストール
/Gemfile
1
2
追加
gem 'carrierwave_backgrounder'
後は $bundle install するだけで終了!、、とはいきません。
いろいろと準備がいるので、そのお話です。
Redisの準備
準備がなかなか大変なのです。
バックグラウンドプロセスを可能にするgemが必要です。
carrierwave_backgrounderは以下に対応。
Delayed Job, Resque, Sidekiq, SuckerPunch, Girl Friday, Qu, Queue Classic
今回はSidekiqを選びます。
そしてSidekiqを使うにはRedisというインラインデータベースを使います。
なので、まずはRedis。
公式サイトのまんまですが、インストールは下記。
1
2
3
4
5
$ cd お好きなディレクトリに
$ wget http://download.redis.io/releases/redis-2.6.16.tar.gz
$ tar xzf redis-2.6.16.tar.gz
$ cd redis-2.6.16
$ make
Redis起動
1
$ src/redis-server
configを指定してくれとのWarningが出るので、
一度control+Cで停止させてから、もう一度起動。
1
$ src/redis-server redis.conf
Sidekiqの準備
/Gemfile
1
2
3
4
5
# 追加
gem 'sidekiq'
- インストール -
$ bundle install
Sidekiqをテストしてみる。
/app/workers/sample_worker.rbを作成。
performを定義します。
1
2
3
4
5
6
7
8
9
10
11
12
class SampleWorker
include Sidekiq::Worker
sidekiq_options queue: :sample
def perform(user_id)
user = User.find(user_id)
name = "userIDは#{user_id}だよ!"
user.update_attribute(:name, name)
end
end
Userモデルがあって、nameカラムがあると想定しています。
/app/controllers/users_controller.rb
1
2
3
4
5
6
7
8
9
10
def create
@user = User.new(user_params)
if @user.save
#これがトリガー
SampleWorker.perform_async(@user.id)
redirect_to @user
else
render 'new'
end
end
上の例だと、Userを新規作成した時に、
sample_workerが動いて、保存されたユーザー名を勝手に書き換えるという処理です。
まぁ実際にはこんなことしないでしょうけど、テストです。
Sidekiqを動かす
1
$ bundle exec sidekiq -q sample
-qオプションにsidekiq_options queue: :sample で付けたキュー名を渡します。
しばらくすると、
Booting Sidekiq 2.15.2 using redis://.......
とかなるはず。
で、先ほどつくったsample_workerが呼び出される処理、
ここでは、ユーザーの新規作成ですが、
ブラウザから実際に操作してみると、、、、
Workers::StoreAsset JID-d0371cf865719ce031d2262a INFO: start
と、バックグランドで処理が動き出したのがターミナルで確認できます。
Workers::StoreAsset JID-d0371cf865719ce031d2262a INFO: done
となったら、処理が終了しています。
実際に保存された名前を見てみると、、
nameが 「userIDは34だよ!」 みたいな名前になってるはず。
OK。準備完了。
Carrierwave_backgrounderを動かす
やっと、準備が整ったので、いよいよ画像のバックグランド処理。
/Gemfile
1
2
3
4
5
6
# 追加
gem 'carrierwave_backgrounder'
-- インストール --
$ bundle install
$ rails g carrierwave_backgrounder:install
/config/initializers/carrierwave_backgrounder.rb
というファイルができたはず。
今回はsidekiqなので、該当箇所のコメントアウトを外します。
carrierwave_backgrounder.rb
1
2
3
4
5
6
7
8
9
CarrierWave::Backgrounder.configure do |c|
#.backend :delayed_job, queue: :carrierwave
# c.backend :resque, queue: :carrierwave
c.backend :sidekiq, queue: :carrierwave
# c.backend :girl_friday, queue: :carrierwave
# c.backend :sucker_punch, queue: :carrierwave
# c.backend :qu, queue: :carrierwave
# c.backend :qc
end
/app/uploaders/user_image_uploader.rb
1
2
3
4
5
6
class UserImageUploader < CarrierWave::Uploader::Base
# - 追加 -
include ::CarrierWave::Backgrounder::Delay
.
.
end
環境によって分ける場合は、こんなのとかでも。
/app/uploaders/user_image_uploader.rb
1
2
3
4
5
6
7
8
9
# Amazon S3
if Rails.env.production? or Rails.env.development?
storage :fog
include ::CarrierWave::Backgrounder::Delay
end
# Local strage
if Rails.env.test?
storage :file
end
モデルも変更します。
/app/models/users.rb
1
2
3
4
5
6
7
8
class User < ActiveRecord::Base
# Image upload
mount_uploader :image, UserImageUploader
# - 追加 -
store_in_background :image
.
.
end
Userモデルにカラムをひとつ追加します。
1
2
3
4
5
6
7
8
9
10
$ rails g migration AddImageTmpToUser image_tmp:string
確認
class AddImageTmpToUser < ActiveRecord::Migration
def change
add_column :users, :image_tmp, :string
end
end
$ rake db:migrate
DONE!
Userを保存するコードをひとつもさわってませんが、これでOK。
あとはこのコマンドを実行するだけ。
1
$ bundle exec sidekiq -q carrierwave
Userを保存してみると、アップロード作業がスルーされて速攻で次の画面に行くかと。
すばらしい!
補足
現在、どんなキューがあるのか知りたい場合は
Sidekiqが素敵なインターフェイスを用意してくれている。
Gemfile
1
2
3
4
5
6
7
gem 'sidekiq'
- 追加 -
gem 'slim', '>= 1.1.0'
gem 'sinatra', '>= 1.3.0', :require => nil
- インストール -
$ bundle install
config/routes
1
2
3
- 追加 -
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
ブラウザで、your_app.com/sidekiq にアクセスすると、
かっこいい画面で確認できるはず。
以上です。
おつかれさまでした。