Rails3.XXで画像アップロード
こんばんはこんばんはue10000です。
Rails3.0.8で画像アップロードしたくなりました。
という訳で今は何が標準?ってことでググッテミマシタ。
あたりがよく使われてるみたいですね。
paperclipのほうが記事も多くデファクトに近い感じです。使い方はattachment_fuとかrails2系で使っていた人には違和感なさそう。機能もデータストアにAmazonS3に対応してたりと盛りだくさん。自前でガリガリ書いてた昔に比べると雲泥の差で楽です。
今回はue10000はdragonflyの方を使うことにしました。paperclipもdragonflyも機能的にほぼ優劣はなさそう。一番大きな違いはpaperclipはアップロード時に画像のリサイズを予めしておく感じ(自分でも同じように考えそう)、dragonflyは初回描画時に画像のリサイズをして、rack/cacheに画像を保存しておく感じ(なるほどそれもありだけど初回の負荷気になるよ)という部分のようです。
今回はデザイン部分が大幅に変わる可能性が高いので、dragonflyを使って画像アップロード機能を実装することにします。あとから画像のサイズ変わってアップロードしなおしとか嫌なんで。
初回描画時とrack/cacheを消した時の負荷が気になりますが、負荷が高いようであれば公開前にabや自前のクローラーでアクセスしておけばOKと割りきっていきます。逆にデザインが大きく変わることがない(しっかりデザインや使用用途が決まっていれば)のであればpaperclipのほうが使いやすそうです。このへんはケースバイケースかなと。チーム体制にもよりそうです。プログラム作る側がある程度デザインまでやってしまって、後からデザイン専門の担当者に修正してもらったりだったりすると、dragonflyのほうが楽かな?機能の追加もデザインの追加・変更もあることを考えると、dragonflyのほうが優秀な気がしてきました。paperclipだってアップロードしなおさなくても、DBの修正と画像サイズのリサイズスクリプトを作っておけば運用中に修正は効きます。dragonflyの方ならビューの簡単な修正(+スペック不足で表示が重くなるなら、事前アクセス)で対応できます。どっちが楽?と考えると、後者のビューの簡単な修正(+スペック不足で表示が重くなるなら、事前アクセス)のほうが楽な気がします。またrails使っていると、かなりの確率でcacheをはさんでいるはずなので。
というわけで実装してみます。
インストール自体は非常に簡単です。公式ドキュメント通りで動きます。
手順としては
OS側でimage-magickをインストール
sudo apt-get install libmagick++-dev imagemagick
Gemfileに以下を追記
gem 'rack-cache', :require => 'rack/cache' gem 'dragonfly', '~>0.9.8'
bundle install
bundle install
起動時にdragonflyが使えるように config/initializers/dragonfly.rb を作成
require 'dragonfly/rails/images'
ここまででインストールは完了です。
では試しにphotoというscaffoldを作成して、動きを試してみます。
rails g scaffold photo invoke active_record create db/migrate/20111113014906_create_photos.rb create app/models/photo.rb invoke test_unit create test/unit/photo_test.rb create test/fixtures/photos.yml route resources :photos invoke scaffold_controller create app/controllers/photos_controller.rb invoke erb create app/views/photos create app/views/photos/index.html.erb create app/views/photos/edit.html.erb create app/views/photos/show.html.erb create app/views/photos/new.html.erb create app/views/photos/_form.html.erb invoke test_unit create test/functional/photos_controller_test.rb invoke helper create app/helpers/photos_helper.rb invoke test_unit create test/unit/helpers/photos_helper_test.rb invoke stylesheets identical public/stylesheets/scaffold.css
画像を扱うモデルを修正します。
ここで出てくる「image_accessor」がdragonflyが提供してくれるメソッドです。
今回は「image_accessor :photo_image」としています。
app/models/photo.rb
class Photo < ActiveRecord::Base image_accessor :photo_image end
続いて実際にDBに値を保存するため、migrationを修正します。
"photo_image_uid"、"photo_image_name"というカラムを追加しています。
ごさっしの通り前述の「モデル側image_accessorで指定した名前+_uid」「モデル側image_accessorで指定した名前+_name」というカラムを作ります。
db/migrate/20111113014906_create_photos.rb
class CreatePhotos < ActiveRecord::Migration def self.up create_table :photos do |t| t.string "photo_image_uid" t.string "photo_image_name" t.timestamps end end def self.down drop_table :photos end end
DBに反映したいのでココらへんでrake db:migrateしてください。
ビューを修正します。アップロード時のフォームを修正。
以下を追加しています。詳細は以下のソース見てね。
「:html => {:multipart => true}」「<%= f.file_field :photo_image %>」
app/views/photos/_form.html.erb
<%= form_for(@photo, :html => {:multipart => true}) do |f| %> <% if @photo.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2> <ul> <% @photo.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <%= f.file_field :photo_image %> <div class="actions"> <%= f.submit %> </div> <% end %>
ここまでで画像のアップロードが動きます。
http://localhost:3000/photos/newとかにアクセスして試してみてください。
次に表示。ここはdragonflyの真骨頂と思える部分。
「モデル名.image_accessorで指定した名前.url」で画像が入ったURL文字列を取得
「モデル名.image_accessorで指定した名前.thumb(リサイズサイズ指定).url」でリサイズされた画像が入ったURL文字列を取得
等々ができます。jpg取得もtiff変換も可能です。
app/views/photos/index.html.erb
<h1>Listing photos</h1> <% @photos.each do |photo| %> <%= image_tag photo.photo_image.url %> <%= image_tag photo.photo_image.thumb('400x200#').url %> <%= image_tag photo.photo_image.jpg.url %> <%= image_tag photo.photo_image.process(:greyscale).encode(:tiff).url %> <%= link_to 'Show', photo %> <%= link_to 'Edit', edit_photo_path(photo) %> <%= link_to 'Destroy', photo, :confirm => 'Are you sure?', :method => :delete %> <% end %> <br /> <%= link_to 'New Photo', new_photo_path %>
http://localhost:3000/photos
で動きを確認することができるはず。
後でスコア撮ってみますが、初回描画時はやっぱ体感レベルで重いな(^_^;)
二回目以降はキャッシュから読み出しているので問題なく早いです。
ちなみに画像のアップロード先フォルダは
public/system/dragonfly/環境名(production,test,development)/日付/ファイル名
となっているようです。
ちなみにdestroy actionもscaffoldのコードで動いちゃいます(これは素直に感心した)。実態の画像ファイルも消してくれます。