トップへ
田村研究室

インターネット利用技術演習

2009年度

2009
6/15

CGIの応用

カウントデータをグラフィックス表示する

準備: 数字画像の用意

アクセスカウンタの表示をグラフィック表示にするには,好みの数字画像を自分で書いたり著作権フリーのものを用意したりして,カウント数に対応した数字画像を表示するようにCGIスクリプトを準備する.

(1)数字画像の選択・ダウンロード

著作権フリーの数字データは、「数字画像」などのキーワードでGoogleで検索すればすぐ見つかる。ただし,利用条件として例えば作成者に連絡したり商用利用が制限されていたりするものも多いので,ページの説明を十分確認すること.例えば、このようなところでフリーのものが配布されている.

(2)数字画像の格納場所の決定

そこからダウンロードされたファイルは,0から9までの10個の数字画像データが圧縮されて一つのファイルとしてダウンロードされるものが多い.その場合,圧縮ファイルを展開しWebページ用のディレクトリにひとつずつばらして保存する.展開すると,例えば数字の1のグラフィックデータとして"1.gif"、2のデータとして"2.gif",以下計10個の数字用のグラフィックデータがあるので,それをWebページ用のディレクトリへ移動する.

Webページ用のディレクトリとは,これまでWebページを格納していたのと同じディレクトリのことで,例えば~/public_html/~/wwwである(「~」は自分のホームディレクトリを表す).または,そのディレクトリの中に作成した画像用(数字用)のディレクトリ(サブディレクトリ)を作成して,そこに保存してもよい.例えば~/public_html/image/などである.各自,自分の環境に合わせて読み替えること.

(3)数字画像の読み出し属性の設定

公開用に保存した数字画像データには,(そのほかのデータファイルと同様に)読み出し属性をつけておくこと.例えばchmodコマンドで設定する.

 cd 数字画像を保存したディレクトリ名
 chmod a+r *

「*」は,全ファイルを意味する.このコマンドを実行すると数字画像を保存したディレクトリ内部の全ファイルに読み出し属性を設定できる.

(4)数字画像へのアクセス確認

この数字画像データが,次のようにWebブラウザからアクセスできることを必ず確認すること.

 http://あなたのページのURL/3.gif

例えば,次のようなアドレスにアクセスして,数字画像が表示できなければならない

 http://サーバ名/~ユーザID/数字画像を格納したディレクトリ名/3.gif

(5)cgiスクリプトの改造

次にCGIスクリプトを書き換える.

#!/bin/sh
echo Content-type: text/html
echo

echo "<html>"
echo "<body>"

カウントデータファイル"count.dat"から、現在のカウント数を変数に読み出す
現在のカウントを格納している変数に+1する

echo カウントの変数 | sed 's/[0-9]/<img src="http:\/\/数字画像データのURL\/&.gif">/g'

変数に格納されている+1されたカウント数をカウントデータファイルに書き戻す

echo "</body>"
echo "</html>"

文字としての数字表示の代わりに,グラフィック表示に置き換える.具体的にはカウント数をechoコマンドでなくsedコマンドを使って<img>タグに置き換える.sedコマンドは,文字列の置換を行うコマンドである.この例では,echoによって表示されるはずの数字一文字一文字([0-9]として指定されている)を,数字画像データのURLを含む<img>タグに置換している.

注意点

数字画像のデータのURLは,前節でアクセスを確認した数字画像のURLのことである.ただし,ディレクトリを区切るための「/」記号は,sedコマンドで別の意味を持っているため,「\」記号を前置させる.「\」記号はそれに続く文字がsedのコマンドに関係しない文字そのものであることを示す(「\」はエスケープ記号と呼ばれる).また,最後の数字データファイル名を指示する所は「&.gif」のように指示することで,置換前の数字に対応する画像データに置き換える.(&は置換前の文字を表す)

sed 's/[0-9]/<img src="http:\/\/サーバ名\/~ユーザID\/数字画像を格納したディレクトリ名\/&.gif">/g'

また、グラフィック表示の場合,5桁表示など固定桁数にしたくなるが,この場合には,「echo カウントの変数」の代わりに、

printf "%05d" カウントの変数

に置き換えれば良い。printfコマンドはechoとは違って書式付きで出力を行うコマンドである。この"%05d"は「頭に0をつけた5桁の整数」を意味する書式情報である。

同時アクセスへの配慮(排他制御処理)

複数の人が同時に特定のCGIページにアクセスすることを考えると、CGIスクリプトが予想外の動作をすることがある。例えば、アクセスカウンタを作成する時に、これまでのカウント数を保存するデータファイルへの読み書きが同時に複数行われると、同じカウント数を読み出したり、正しいカウント数を保持できなくなるおそれがある。

例えば,次のようにほぼ同時にAというアクセスとBというアクセスがあったとする.Aの方が先で,少し遅れてBがアクセスした状況である.

AAの変数データファイルの中身Bの変数B
カウント数を読出5←5
カウント数に+1 65→5カウント数を読出
書き戻し6→66カウント数に+1
6← 6書き戻し

本来ならば,AとBの2つのアクセスがあったため,カウントは合計で+2されなければならない.しかし,上の図では結局+1しかされていない.Aがデータを読み出してから+1する一瞬の間に,Bが読み出しを行っているため,カウント数を誤認することが問題である.この問題を避けるためには,Aがカウント数を+1するまでは,Bに読み出しをさせないようにするほかない.

そこで、同時にアクセスされることを防ぎ、だれか一人がカウント処理しているときには、他の人のアクセスを待たせ、一人の処理が終わった後に順々に他の人を処理していけばよい。これを排他制御と呼ぶ

AAの変数データファイルの中身Bの変数B
カウント数を読出5←5
カウント数に+1 65Aの 終了待ち
書き戻し6→6Aの終了待ち
6→6カウント数を読出
67カウント数に+1
7← 7書き戻し

こちらの場合には,Aの処理が終わるまでBを待たせているため,正しくカウント数は+2される.

排他制御は、ロックファイルを使う方法が良く用いられる。ロック(lock)すなわち「鍵」を模したもので、最初の一人がアクセスしたときに、決められた場所の決められた名前のファイルを作成する(「鍵をかける」)。もしも、すでにその場所にファイルが存在する場合には、すでに誰かが処理中であることを意味するため、他の人はそのファイルが消滅するまで待つ。ファイルを作成した(鍵をかけた)人の処理が終わったとき、その人(に対応したCGIスクリプト)がファイルを消去する(「鍵をはずす」)。その後は、他の人は早い者勝ちで、他の人より先に鍵をかけることができた人が処理を始める。

シェルスクリプトではlockfileコマンドを使ってこれを実現できる。使い方は次の通り。

#!/bin/sh
echo Content-type: text/html
echo

if lockfile -1 -r 5 -l 10 "/tmp/自分のID.lock" ; then

    カウンタ処理の中身

fi
rm -f "/tmp/自分のID.lock"

lockfileコマンドは、指定したファイル(この例では/tmp/ 自分のID.lock)がなければ作成し、もしもすでに存在すれば、消滅するまで待つ、という動作をする。

CGIスクリプトの中でlockfileコマンド使用する場合、ロックファイルは書き込み属性のあるディレクトリを指定しないと、(ファイルを書き込めないので)失敗してしまう。UNIX では通常一時的なファイルを書き込むために,/tmpディレクトリが用意されている。しかし,/tmpディレクトリは複数のユーザで共有しているディレクトリであるため,同じ名前のファイルを指定すると,うまく動作できない.そのため,例のように自分のIDに関係するような名前を指定し,自分専用のロックファイルを用意する.

例のようにif文を使っておくと、ファイルの生成に成功した時に、カウンタ処理を実行することが可能となる。

lockfileコマンドのオプションは、

lockfile -(ウェイト秒数) -r (リトライ回数) -l (タイムアウト秒数) "ロックファイル名"
ウェイト秒数は、もしもロック中だった(ロックファイルが存在するとき)に、何秒待ってから再挑戦するか指示で、その再挑戦を何回まで行うかをリトライ回数に指示、もしもタイムアウト秒数以上前からロックファイルが存在した場合にはなにかトラブルを考えて、ロックファイルを強制削除することを意味する。

このlockfileコマンドは、システムによっては存在しないコマンドである。よく似たコマンドとして「lockf」コマンドが用意されているシステムもある。両者とも詳細は、各システムのmanコマンドで調べればよい。