Ruby で CGI > 送受信_環境変数 ENV > 問題
ImageMagick を使うと、コマンドラインから画像を処理できます。これを、CGI でできるようにしてみましょう。
例えば、-wave (振幅)x(波長) オプションで画像を波打たせることができます。
convert -wave 10x20 before.jpg after.jpg | ||
before.jpg (手前が筆者) |
⇒ | after.jpg |
♪
♪
下記のCGIと同じディレクトリ内に jpeg 画像ファイル before.jpg を置きます
#!/usr/local/bin/ruby
infile = "before.jpg" # 加工前画像ファイル
outfile = "after.jpg" # 加工後画像ファイル
q = ENV['QUERY_STRING'] # アドレスの?以降を取得
system("convert", "-wave", q, infile, outfile) # 画像を加工(波打ち)
Process.waitall # 子プロセス「system()」の処理が終わるのを待つ(ruby1.7以降)
# ↓画像を出力
print <<KEYWORD
Content-Disposition: filename=#{ outfile }
Content-type: image/jpeg
Content-Length: #{ File.size( outfile ) }
KEYWORD
f = File.open( outfile, "rb")
STDOUT.binmode
while line = f.gets do
print line
end
f.close
しかし、この方法には問題点があります。
そこで、クエリーをチェックする仕組みを入れると、下記のようになります。画像処理が失敗した場合にも備えてみました。
#!/usr/local/bin/ruby
infile = "before.jpg"
outfile = "after.jpg"
q = ENV['QUERY_STRING']
if /\A\d+x\d+\Z/ =~ q.to_s then # (数字)x(数字) の形になっているかどうかを確認
r = system("convert", "-wave", q ,infile, outfile) rescue false
outfile = infile unless r # 画像処理に失敗した場合、入力画像を表示
Process.waitall
else
outfile = infile # クエリーのパターンが違っていたら、入力画像を表示
end
print <<KEYWORD
Content-Disposition: filename="#{ outfile }"
Content-type: image/jpeg
Content-Length: #{ File.size( outfile ) }
KEYWORD
f = File.open( outfile ,"rb")
STDOUT.binmode
while line = f.gets do
print line
end
f.close
\A\d+x\d+\Z
の、
\A
は比較される文字列の最初、
\Z
は比較される文字列の末尾、
\d
は任意の数字ひと文字のみ、
\d+
で任意の数字
を、表していますsystem( )
は処理に失敗した場合、ruby1.8以前では、false を出力しますが、ruby1.9以降では例外を発生(=エラーで止まる)させます。
そこで、互換性を意識して、rescue で例外の発生に備えます。(この文章を執筆時のrubyの最新バージョンは 1.8.5 )ひょっとして、同時に複数のアクセスがあると、処理後のファイルがぶつかってしまうかもしれません。処理毎に出力するファイル名を別々にすると回避できるでしょう。
#!/usr/local/bin/ruby
infile = "before.jpg"
outfile = "/tmp/%08d.jpg" % rand( 10**8 ) # 共用一時保存ディレクトリに乱数ファイル名
q = ENV['QUERY_STRING']
if /\A\d+x\d+\Z/ =~ q.to_s then
r = system("convert", "-wave", q ,infile, outfile) rescue false
outfile = infile unless r
Process.waitall
else
outfile = infile
end
print <<KEYWORD
Content-Disposition: filename="wave.jpg"
Content-type: image/jpeg
Content-Length: #{ File.size( outfile ) }
KEYWORD
f = File.open( outfile ,"rb")
STDOUT.binmode
while line = f.gets do
print line
end
f.close # 直後にファイルを削除するので、必ず閉じる
File.delete( outfile ) if outfile != infile && FileTest.file?(outfile)
/tmp/
というディレクトリがあり、ログインできる人なら誰でも一時的にファイルを置くことができます/tmp/
が使えれば好都合です/tmp/
に置かれたファイルは、一定時間が経過すると自動的に削除されます。しかし、管理者の設定によっては、かなり長期間にわたって削除されなかったり、全く削除されなかったりするので、用がすんだら、すぐにファイルを削除するようにするのが良いでしょう。File.delete( outfile )
で、outfile が削除されます。
しかし、outfile が存在していないと、例外が発生(エラー)してしまうので、
FileTest.file?(outfile)
で、その存在を確認しておきます。
また、outfile が infile と同じだった場合は、削除するとマズいので、そのケースも除外しておきます。/tmp/
ディレクトリはありませんが、ファイルの保存には困らないので、/tmp/
の部分を消してください明るさ、彩度、シャープネスの調整ができるようにしてみましょう
#!/usr/local/bin/ruby
infile = "before.jpg"
outfile = "/tmp/%08d.jpg" % rand( 10**8 )
qs = ENV['QUERY_STRING'].to_s
a = Array.new
a += ["-gamma", ([$1.to_f/100]*3).join("/")] if /\bb=(\d+)\b/ =~ qs # b=140; ⇒ `-gamma 1.4/1.4/1.4`
a += ["-modulate", "100/#{ $1 }/100"] if /\bc=(\d+)\b/ =~ qs # c=115; ⇒ `-modulate 100/115/100`
a += ["-sharpen", $1] if /\bs=(\d+(\.\d+)?(x\d+(\.\d+)?)?)\b/ =~ qs # s=2x1; ⇒ `-sharpen 2x1`
if a == [] then
outfile = infile
else
a = ["convert"] + a
a += [infile, outfile]
r = system(*a) rescue false
outfile = infile unless r
Process.waitall
end
print <<KEYWORD
Content-Disposition: filename="photo.jpg"
Content-type: image/jpeg
Content-Length: #{ File.size( outfile ) }
KEYWORD
f = File.open( outfile ,"rb")
STDOUT.binmode
while line = f.gets do
print line
end
f.close
File.delete( outfile ) if outfile != infile && FileTest.file?(outfile)
self.to_f
はself
を浮動小数点数にします。すなわち、"2".to_f
は2.0
[ 1.4 ]*3
は繰り返し[ 1.4, 1.4, 1.4 ]
になります[ "A", 3, "B" ].join("@")
は、配列の要素がキーワード"@"
で結合されて、"A@3@B"
になります。さらにいろんな加工ができるようにすると、ImageMagick でのコマンドの書き方に準拠して、
#!/usr/local/bin/ruby
infile = "before.jpg"
outfile = "/tmp/%08d.jpg" % rand( 10**8 )
qs = ENV['QUERY_STRING'].to_s
a = Array.new
a += ["-crop", $1] if /\bcrop=(\d+%|\d+x\d+\+\d+\+\d+)\b/ =~ qs
a += ["-roll", $1] if /\broll=((\+|\-)\d+(\+|\-)\d+)\b/ =~ qs
a += ["-rotate", $1] if /\brotate=((\+|\-)\d+)\b/ =~ qs
a += ["-flip" ] if /\bflip\b/ =~ qs
a += ["-flop" ] if /\bflop\b/ =~ qs
a += ["-antialias" ] if /\bantialias\b/ =~ qs
a += ["-charcoal", "1"] if /\bcharcoal\b/ =~ qs
a += ["-colorize", $1] if /\bcolorize=(\d+\/\d+\/\d+)\b/ =~ qs
a += ["-contrast" ] if /\bcontrast\b/ =~ qs
a += ["-edge", $1] if /\bedge=(\d+)\b/ =~ qs
a += ["-emboss", $1] if /\bemboss=(\d+)\b/ =~ qs
a += ["-equalize" ] if /\bequalize\b/ =~ qs
a += ["-implode", $1] if /\bimplode=(\d+)\b/ =~ qs
a += ["-monochrome" ] if /\bmonochrome\b/ =~ qs
a += ["-negate" ] if /\bnegate\b/ =~ qs
a += ["-median", $1] if /\bmedian=(\d+)\b/ =~ qs
a += ["-raise", $1] if /\braise=(\d+x\d+)\b/ =~ qs
a += ["-segment", $1] if /\bsegment=(\d+x\d+)\b/ =~ qs
a += ["-shade", $1] if /\bshade=(\d+x\d+)\b/ =~ qs
a += ["-spread", $1] if /\bspread=(\d+(\.\d+)?)\b/ =~ qs
a += ["-threshold", $1] if /\bthreshold=(\d+)\b/ =~ qs
a += ["-wave", $1] if /\bwave=(\d+x\d+)\b/ =~ qs
a += ["-swirl", $1] if /\bswirl=(\-?\d+)\b/ =~ qs
a += ["-interlace", $1] if /\binterlace=(none|line|plane|partition)\b/ =~ qs
a += ["-gamma", $1] if /\bgamma=(\d+(\.\d+)?\/\d+(\.\d+)?\/\d+(\.\d+)?)\b/ =~ qs
a += ["-gamma", ([$1.to_f/100]*3).join("/")] if /\bb=(\d+)\b/ =~ qs
a += ["-modulate", $1] if /\bmodulate=(\d+\/\d+\/\d+)\b/ =~ qs
a += ["-modulate", "100/#{ $1 }/100"] if /\bc=(\d+)\b/ =~ qs
a += ["-geometry", $1] if /\bgeometry=(\d+%|\d+x\d+((\+|\-)\d+x\d+)?)\b/ =~ qs
a += ["-sharpen", $1] if /\bsharpen=(\d+(\.\d+)?(x\d+(\.\d+)?)?)\b/ =~ qs
a += ["-sharpen", $1] if /\bs=(\d+(\.\d+)?(x\d+(\.\d+)?)?)\b/ =~ qs
a += ["-blur", $1] if /\bblur=(\d+(\.\d+)?(x\d+(\.\d+)?)?)\b/ =~ qs
if /\btransparent=([a-f0-9]{3,9})\b/ =~ qs then a += ["-transparent", "#" + $1] elsif
/\btransparent=([a-z]+)\b/ =~ qs then a += ["-transparent", $1] end
if /\bborder=(\d+x\d+)\b/ =~ qs then a += ["-border", $1] end
if /\bbordercolor=([a-f0-9]{3,9})\b/ =~ qs then a += ["-bordercolor", "#" + $1] elsif
/\bbordercolor=([a-z]+)\b/ =~ qs then a += ["-bordercolor", $1] end
if /\bframe=(\d+x\d+\+\d+\+\d+)\b/ =~ qs then a += ["-frame", $1] end
if /\bmattecolor=([a-f0-9]{3,9})\b/ =~ qs then a += ["-mattecolor", "#" + $1] elsif
/\bmattecolor=([a-z]+)\b/ =~ qs then a += ["-mattecolor", $1] end
if a == [] then
outfile = infile
else
a = ["convert"] + a
a += [infile, outfile]
r = system(*a) rescue false
outfile = infile unless r
Process.waitall
end
print <<KEYWORD
Content-Disposition: filename="after.jpg"
Content-type: image/jpeg
Content-Length: #{ File.size( outfile ) }
KEYWORD
f = File.open( outfile ,"rb")
STDOUT.binmode
while line = f.gets do
print line
end
f.close
File.delete( outfile ) if outfile != infile && FileTest.file?(outfile)
以上です。