Railsで画像データをMySQLに保存する方法

概要

DBにMySQLを使用したRailsを開発に使っておりますが、ある時画像を取り込む必要が出てきました。その時に調べたこととその結果をまとめてみました。

環境

 OS      :debian 5.0.3
 ruby    :1.8.7
 rails   :2.3.2
 rmagick :2.12.2

マイグレーション

まずはマイグレーションを使ってテーブルの準備。いつも通りです。

 $ cd RAILS_ROOT
 $ ./script/generate migration Image
 $ vi ./db/migrate/VERSION_image.rb
class Image < ActiveRecord::Migration
  def self.up
    create_table :images do |t|
      t.binary :image
    end
  end
  def self.down
    drop_table :images
  end
end
 $ rake db:migrate

これで、カラムにIDとimageを持つテーブルimagesが出来ました。次はこのテーブルに画像データを突っ込む*1ためにscaffoldを作成します。なんと、ここでRails2.0ではscaffold作成時にmigrationもやってくれるらしいことに気づく。まじっすか。
悲しんでても仕方ないので、rollbackをしかける。

 $ rake db:rollback

よし、テーブルはなくなった。気を取り直してscaffoldです。

 $ ./script/generate scaffold Image image:binary
 $ rake db:migrate
 $ ./script/server

http://localhost:3000/ にアクセスし、サーバが起動していることを確認出来たらいよいよじっそうです。

取り込み

画像があるディレクトリをHOGEとします。この画像を取得するのに、RMagickを使用します。とりあえず、画像を取得して別名に保存するスクリプトを書いて実験。データベースには手でデータを入れる。Imageモデルにこんなコードを書く。

require 'rubygems'
require 'RMagick'
class Image < ActiveRecord::Base
  def save
    png = Magick::ImageList.new("HOGE/hogehoge.png")
    image = Image.new
    image.image = png.to_blob
    image.save!
  end
end

こんな感じで試してみる。いや、この作り方がうまくないのはわかるんだが…ま、テスト用ってことで。とりあえず画像が保存されることを確認した。バイナリデータはこれが正しく保存されているかどうか確認できないから嫌だ…

表示

今度はDBから画像を取得して画面に表示する。さっきと同じクラスの中にgetメソッドを作ります。

  def get id
    image = Image.find(id)
    img = Magick::Image.from_blob(image.image).shift.to_blob
  end

コントローラはこんな感じ。send_dataしてあげるのがポイント。画像表示のタイミングでget_imageメソッドにアクセスして、ビューにバイナリ画像を送るようなイメージです。

  def get_image
    img = Image.new
    @image = img.get
    send_data(@image, :disposition => "inline", :type => "image/png")
  end

最後にビューです。indexビューです。

<h1>Image</h1>
<%= image_tag(url_for(:controller => 'images', :action => 'get_image', :format => :png)) %>

これで http://localhost:3000/images/ にアクセスすると、無事画像が表示されていました!うーん。blobはphpで大分苦労した覚えがあるんですが、Railsだと割とあっさりいけてしまいました。

つーことで、目的は達成したので業務に戻りますです。

*1:imageカラムの属性にbinaryを選ぶとblobとなりますが、容量が64KBまでとなります。もっと大きなサイズの画像を扱いたい場合はまた別の方法で。