UpImage.cgi

出典: YKAwiki

概要

Flash から画像ファイルと保存先ファイル名を受け取り、ファイルを作成して書き込む CGI 。Ruby を使用する。

Flash 側で既にファイルサイズ、参照するファイル名などについて適切かどうかはチェックしているが、直接 CGI を触られた場合に備えファイルサイズとファイル名の先頭文字だけは再検査する。

(stub) http://ja2yka.homeip.net/~taka/cgi-bin/ruby/upImage.html で画像のアップロードができます。

また、アップロードした画像は http://ja2yka.homeip.net/~taka/cgi-bin/ruby/loaderTest.swf で確認できます。(この画像をロードする swf をタイピングゲーム本体に組み込む予定)

ただし *.png 以外は表示されない場合があるでしょう。また、画像を消したい場合は上書きするしかないので注意。

open() について。Ruby では特に `|' がヤバイ(と思う)。

Rubyリファレンスマニュアル-組み込み関数 の、文中「ファイル名が `|' で始まる時には...」を参照)

ソース

解説はちょっと待ってね

#!/usr/local/bin/ruby

# アルファベット大文字 ([A-Z]) で始まる識別子は定数。
# 大盤振る舞いの 300kB
FILE_MAX_SIZE = 300*1024

# cgi ライブラリの使用
require "cgi"

# .html のほうで
# <form method="post" action="./upImage.cgi" enctype="multipart/form-data">
# で渡しているので注意(画像ファイルのバイト列、画像の名前は添付ファイルとして渡されている。)
formData = CGI.new

print "Content-type: text/html\n\n"
puts "image uploader"

puts "uploading..."
puts "</br>"

# Flash から渡された'imageData' から、ハッシュ(連想配列)操作的に
# 画像データを StringIOオブジェクトとして取得
imageData = formData['imageData']
if imageData.size > FILE_MAX_SIZE then
	puts "too large file"
	exit
end

puts "imageData size ok. size: "
puts imageData.size
puts "</br>"

# 同様に Flash から渡された'imageName' から、
# 画像の名前のための文字列を取得。
# この名前のファイルを作成し、書き込む。
fileName = formData['imageName']
if fileName.size < 6 then
	puts "too short fileName"
	exit
end

# ファイル名が `|' で始まる時には続く文字列をコマンドとして起動し、
# コマンドの標準入出力に対してパイプラインを生成する。
# Flash 側で `|' で始まるファイル名は作成されないようになっているが、
# Flash 以外からアクセスされた時に怖いのでちょっとした処理を。

# StringIO を文字列に直す。
fileName2 = fileName.read

if fileName2[0, 1] == "|" then
	fileName2 = "illegal_fileName_" + fileName2
	puts fileName2
end

fileName2 = "./" + fileName2

puts "fileName length ok. fileName : "
puts fileName2
puts "</br>"

# 書き込み先ファイル fileName2 をオープン。File オブジェクトを返す。
# "wb": ファイルをバイナリ書き込みモードでオープン。
# ファイルがすでに存在していればその内容を空にして書き込む。
# ファイルのアクセス権はデフォルト 0666 。
# open をブロックとともに呼び出すことで、
# ファイルをオープンしてブロックを実行し、
# ブロックの実行が終了するとファイルをクローズする。
# f はイテレータ
# puts は File のメソッドでなく組み込み関数。

open(fileName2, "wb") do |f|
	f.puts imageData.read
end

puts "uploaded."
puts "</br>"
puts "http://ja2yka.homeip.net/~taka/cgi-bin/ruby/loaderTest.swf でチェックしてみて"

Flash から呼ぶとなんかおかしい

Flash からファイルをアップロードしようとしたところ、冒頭の

formData = CGI.new

でエラーが出てなんともかんとも。サーバーのエラーログは以下の通り

/usr/local/lib/ruby/1.8/cgi.rb:979:in `read_multipart'
no content body
(
EOFError
)
from /usr/local/lib/ruby/1.8/cgi.rb:1104:in `initialize_query'
from /usr/local/lib/ruby/1.8/cgi.rb:2270:in `initialize'
from /home/taka/public_html/cgi-bin/ruby/upImage.cgi:23

最後の upImage.cgi:23 が formData = CGI.new のところ。

で、問題の /usr/local/lib/ruby/1.8/cgi.rb:979近辺は以下の通り

# start multipart/form-data
      stdinput.binmode if defined? stdinput.binmode
      boundary_size = boundary.size + EOL.size
      content_length -= boundary_size
      status = stdinput.read(boundary_size)
      if nil == status
        raise EOFError, "no content body"
      elsif boundary + EOL != status
        raise EOFError, "bad content body"
      end

stdinput は stdin のエイリアス。HTML ならエラーでないのに、Flashではなぜか文字列が読めてない感じ?Flash がどうやって渡してるのかわからん。POST リクエストに関しては

multipart/form-data の Content-Type エレメント 
name 属性が "Filedata"、filename 属性が元のファイル名に設定された Content-Disposition エレメント 
ファイルのバイナリコンテンツ 

としかヘルプに書いてない。 わからん・・ということで php に逃げます。