Herokuは、単体ではファイルをサーバー上に永続的に保存することはできない1ので、RMagickを使って画像を生成してもそれを保存することはできない。ただし、生成した画像をその場で単に表示するだけの目的であれば、永続的に保存する必要はないので、次の2つの方法でRMagickで生成した画像を利用できる。
- 生成した画像を保存せずにバイナリデータを直接表示する方法
- 生成した画像を一時保存フォルダに保存して表示する方法
生成した画像を保存せずにバイナリデータを直接表示する方法
生成したRMagickのImage
オブジェクトを、to_blob
メソッドを使って、直接メモリー上にバイナリデータとして展開し、それを出力する方法。これはメモリーの消費量が多そう。毎回1度だけ表示するようなジェネレータ系には適しているかも。この方法のサンプルコードが以下。
require 'rubygems'
require 'sinatra'
require 'RMagick'
text = 'Hello, World'
size = 30
get '/pic1' do
blob = create_pic
content_type "image/png"
blob #=> バイナリデータを直接表示
end
def create_pic
img = Magick::Image.new(400, 300) { self.background_color = '#336699' }
img.format = 'png'
draw = Magick::Draw.new
draw.annotate(img, 0, 0, 50, 100 + size, text) do
self.font = 'Verdana-Bold'
self.fill = '#FFFFFF'
self.align = Magick::LeftAlign
self.stroke = 'transparent'
self.pointsize = 30
self.text_antialias = true
self.kerning = 1
end
img.to_blob #=> バイナリデータ化
end
生成した画像を一時保存フォルダに保存して表示する方法
もうひとつの方法は、こちらの記事で知ったのだけど、Herokuでは/tmp
フォルダのみ一時保存フォルダとして利用することが可能で、そこにはファイルを書き出せるとのこと。実際にやってみたら出来た。一時保存なので時間が経過すると消去される。消去された画像にアクセスしようとすると、Internal Server Errorが起きた覚え。
/tmp
フォルダのファイルが消去される条件は、自分で試してみた2限りでは、アプリがスピンダウン状態になると消去されるっぽくて、そのスピンダウン状態には、最後にサイトにアクセスがあってから一定時間が経過するとなる。その一定時間は正確にはわからないけど、感覚的には10~30分くらいかな?ここらへん、詳しい人がいたら教えてほしい。というわけで、生成した画像を/tmp
フォルダに一旦書き出し、それを再度読み込んで表示するというサンプルコードが以下。
require 'rubygems'
require 'sinatra'
require 'RMagick'
text = 'Hello, World'
size = 30
get '/pic1' do
path = create_pic
file = File.open(path, "rb") {|f| f.read } #=> 一時保存したパスから画像を読み込み
content_type "image/png"
file #=> 読み込んだ画像を表示
end
def create_pic
img = Magick::Image.new(400, 300) { self.background_color = '#336699' }
img.format = 'png'
draw = Magick::Draw.new
draw.annotate(img, 0, 0, 50, 100 + size, text) do
self.font = 'Verdana-Bold'
self.fill = '#FFFFFF'
self.align = Magick::LeftAlign
self.stroke = 'transparent'
self.pointsize = 30
self.text_antialias = true
self.kerning = 1
end
path = './tmp/temp.png' #=> /tmpフォルダ内(配下)じゃないと書き込めない
img.write(path) #=> 一時保存フォルダに一旦書き出し
path #=> 一時保存したパスをリターン
end
どっちの方法がいいかは利用する状況によると思うけど、生成した画像に複数回アクセスするのであれば、後者のほうが/tmp
フォルダに保存された画像が存在する限りはそれを使いまわせるので、リソースの消費は抑えられそう。ただし、ファイルの書き込みと読み込みのオーバーヘッドが発生するので、1回目の処理(画像生成部分)は遅くなりそう。全部憶測なのは、こういう場合のベンチマークの取り方を知らないから…。もし、上記以外にも良い方法があれば教えてください。