2010-02-07DBに格納したバイナリ画像データを表示させる方法
DBに格納したバイナリ画像を表示させる方法
rails |
最近のDBはミラーリングやレブリケーションが当たり前にされてるので
DBにバイナリで画像格納ってのはよくある方法だと思う。
概要
1.controllerでバイナリデータ以外をselect
2.view側でurl_forによりcontrollerで定義した画像表示actionを指定
3.view側のimage_tagで表示
4.通常のhtmlレンダリング時に、画像表示actionがリクエストされると
controllerのsend_dataでは、id毎にバイナリデータを検索してviewに返す
この流れでは、画像毎にselectが走るが
まぁ、致し方ないか。
controller
selectオプションでバイナリデータ以外の項目を取得。
def index #@images = Image.all @images = Image.all(:select => 'id, name, updated_at') respond_to do |format| format.html # index.html.erb format.xml { render :xml => @images } end end
def get_image @image = Image.find(params[:id]) send_data(@image.image, :disposition => "inline", :type => "image/png") end
view
<% @images.each do |image| %> <tr> <td><%=h image.name %></td> <td><%= image_tag(url_for(:action => 'get_image', :id => image.id), :size => '100x100')%></td> <td><%= link_to 'Show', image %></td> <td><%= link_to 'Edit', edit_image_path(image) %></td> <td><%= link_to 'Destroy', image, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %>
こんな感じ
パチパチ
あれ?
生成されたhtmlを見ると
image_tagで通常表示した際に付く、画像のタイムスタンプが無い。
そうか、バイナリだもんな。
これだと、タイムスタンプによるキャッシュが使えない。
なんとかDB更新日時渡せないかな、と↓を思い返しながら思案。
passengerのcss - うんたらかんたらRuby - Rubyist
image_tagをオーバーライドしてやってみた。
view側でaddsrcオプションを指定。
<%= image_tag(url_for(:action => 'get_image', :id => image.id), :size => '100x100', :addsrc => image.updated_at.to_i.to_s) %>
helper
addsrcを追記して、最後に消してみた。
#override method #actionpack-2.3.5/lib/action_view/helpers/asset_tag_helper.rb def image_tag(source, options = {}) options.symbolize_keys! options[:src] = path_to_image(source) options[:alt] ||= File.basename(options[:src], '.*').split('.').first.to_s.capitalize if size = options.delete(:size) options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$} end if mouseover = options.delete(:mouseover) options[:onmouseover] = "this.src='#{image_path(mouseover)}'" options[:onmouseout] = "this.src='#{image_path(options[:src])}'" end # added start if options[:addsrc] if /\?/ =~ options[:src] options[:src] << "#{options[:addsrc]}" else options[:src] << "?#{options[:addsrc]}" end options.delete(:addsrc) end # added end tag("img", options) end
んで
どうなんでしょう。こういうやり方って。
使用箇所を最小限にするならありなんでしょうか?
file_columnとか使うと幸せになれるんだっけ?
参考
【Rails:6】コントローラとビュー(応用編) - L’Isle joyeuse
2010/03/12追記
viewでurl_forを使って画像呼出しメソッド呼んでたけど
image_tag("/get_image/#{id}", options) みたいに書けるようだ。
これでもいいね。
トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100207


1. public/images/hoge/以下に画像ファイルがなかったらDBからバイナリを取って画像を出力する(File.utime()必要)。
2. 画像ファイルがあったらDBからupdated_atだけ引いて比較する。DBの方が新しい場合はファイルを上書きする(File.utime()必要)。
3. 画像ファイルのURLパスを返す。
ってのを実行するimage_path()ってhelperを作るのはどうでしょうか?
これだと、httpdに画像を任せられて速いし、DBに負荷が余りかからないし、分散環境でも問題ないです。
なるほど。DBにはマスタデータを格納し、WEBサーバ毎にディスクに画像持たせるという方式ですね。
ディスクIOが気にならない程度なら、それもよさそうですね。
でもディスクの画像を使用するのが前提なら、共有ディスクに置いたりするってのもよさそうですね。
disk_cache、memcachedなどキャッシュ使用を視野に入れるとさらに面白そうです。
いずれにせよ、実際に使う局面でベンチマークとってみたいところです(まだサービスインできそうなアイデアがない。。。早く纏めたいな。)。
是非使ってみたいとは思ってますが、リバースプロキシが必要な状況(スケールさせる)っていうのがなかなか。