RubyでActiveRecordを使ってSQLite3に簡単に画像を入れようしたらダメでした。で、結論を言うとImageMagickでto_blobを使用しないとダメでした。勉強した事を書いておく。

require "sqlite3"
require "active_record"
require "RMagick"
require "pry"
include Magick

class Sample < ActiveRecord::Base
  establish_connection :adapter  => 'sqlite3',
                       :database => './test.db'
end

name = "input"
input = Image.read("#{name}.jpg").first

hoge = Sample.new
hoge.name = name
hoge.image = input.to_blob
hoge.save

# binding.pry

blob = Sample.find_by(:name => name).image
output = Image.from_blob(blob).first
output.write("output.jpg")

// #import.sql
// create table samples (
// id integer primary key,
// imgcode text,
// image blob
// );

最初は単純にFile.open("target.jpg","wb)とかでいけちゃう?と思ったけど、エラー。少し考えて「Ruby上にデータとして持たないとSQLite3に入れられないよね」と思い、それにはImageMagickを使わなきゃダメという結論だった。

Magick::Image.read("target.jpg").firstで画像を開き、ActiveRecordでレコードに書き込む際にto_blobメソッドを使ってdbに格納可能なデータに変換。

そして、呼び出す時にはMagick::Image.from_blob(foo).firstしたら無事input.jpgが一旦sqlite3に格納された後、取り出してoutput.jpgに保存できた。

この記事書いている最中に「あれ?これmini_magickでもいいんじゃね?」と思った。なので試してみた。初めてbenchmark使ってみる。

require "sqlite3"
require "active_record"
require "RMagick"
require "mini_magick"
require "benchmark"
require "pry"

class Rtest < ActiveRecord::Base
  establish_connection :adapter  => 'sqlite3',
                       :database => './testr.db'
end

class Minitest < ActiveRecord::Base
  establish_connection :adapter  => 'sqlite3',
                       :database => './testmini.db'
end

def rmagick_test
  100.times do
    name = "input"
    input = Magick::Image.read("#{name}.jpg").first

    hoge = Rtest.new
    hoge.imgcode = name
    hoge.image = input.to_blob
    hoge.save

    getfile = Rtest.find_by(:imgcode => name).image
    output = Magick::Image.from_blob(getfile).first
    output.write("output.jpg")
  end
end

def minimagick_test
  100.times do
    name = "input"
    input = MiniMagick::Image.open("#{name}.jpg")

    hoge = Minitest.new
    hoge.imgcode = name
    hoge.image = input.to_blob
    hoge.save

    getfile = Minitest.find_by(:imgcode => name).image
    output = MiniMagick::Image.read(getfile)
    output.write("output.jpg")
  end
end

Benchmark.bmbm(15) do |x|
  x.report("rmagick_test") {rmagick_test}
  x.report("minimarick_test") {minimagick_test}
end

# Rehearsal -------------------------------------------------
# rmagick_test    2.300000   1.360000   3.660000 (  8.552026)
# minimarick_test 0.250000   2.060000   5.050000 ( 15.261154)
# ---------------------------------------- total: 8.710000sec

#                     user     system      total        real
# rmagick_test    2.290000   1.100000   3.390000 (  7.268883)
# minimarick_test 0.280000   1.380000   3.510000 ( 11.598356)

MiniMagickはfrom_blobじゃなくてreadじゃないとダメだって一度怒られた。あと.gifアニメとかの連続データが確か扱えないのでfirstをつけんなとも怒られた。コード自体はMiniMagickの方が綺麗で好きだ。

1つのファイルじゃ差が分からないと思ったので、100回ほどファイルを出し入れし、更に勉強も兼ねてリハーサルを入れたbmbmメソッドを使ってみた。

見てみるとuserというスコアではminimagickが優っている物の、総合的なスコアはRMagickを使った方が早いというふうに見える。このuserとsystemとtotalは__user=計算時間、system=入出力時間、real=コマンド起動から終了までの経過時間__という事らしい。なるほどわからん。

ガベージコレクション?によって結果が左右されるみたいなので、精密なスコアでは無いみたいだが、何度かやってもRMagickの方がrealスコアが良いので、この処理に関してはRMagickで良さそう。

なお、まだまだ未熟者故にこのベンチマークの取り方自体が不毛だったり間違っていたりする可能性がある。また後日Benchmarkについては調べてみたい。

パーフェクトRuby (PERFECT SERIES 6)
Rubyサポーターズ すがわら まさのり 寺田 玄太郎 三村 益隆 近藤 宇智朗 橋立 友宏 関口 亮一
技術評論社
売り上げランキング: 23,821