DBに格納したバイナリ画像を表示させる方法

最近の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
added start から addes endの箇所。
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とか使うと幸せになれるんだっけ?

2010/03/12追記

DBに入れた画像を表示する - 篳篥日記


viewでurl_forを使って画像呼出しメソッド呼んでたけど
image_tag("/get_image/#{id}", options) みたいに書けるようだ。
これでもいいね。