Perl/CGIプログラムの入力処理(2)

今回は、Perl/CGIプログラミングをする上で、知っておくと便利なCGIモジュールまたはCGI.pmというモジュールの使い方を学習します。

  1. 編集前記
  2. CGIモジュールの概要
  3. CGIモジュールでデータの取得
  4. CGIモジュールでHTML出力
  5. 編集後記

編集前記

今回はCGIモジュールの使い方について学習するということなので、この編集前記では改めてPerl/CGIプログラミングをする上でモジュールを使うメリットについて書こうと思います。

モジュールとは、ある目的を持ったプログラムの集まりのことです。

↑いろいろと細かいことを犠牲にしつつ一言で説明するとこうなります。

そしてPerl/CGIプログラミングで使われる各種モジュールは、以下のサイトで公開されています。

The CPAN Search Site(英語)

http://search.cpan.org/

たくさんのモジュールが無料で一般公開されているので、Perl/CGIプログラミングを学習したいあなたはぜひ見ておいてください。

今回はCGIモジュールというPerl/CGIプログラミングの中でも基本的なモジュールを扱いますが、比較的歴史のあるPerl言語にはいろいろなモジュールがあります。

もちろんこのサイトでもいくつかのモジュールを使っています。

例えば、 テキストファイルからMIDIファイルを作成できるPerl/CGIプログラム を公開しています。

これは、MIDIモジュールを使ってPerl/CGIプログラムからMIDIファイルを作成しています。

MIDIモジュールの公開ページ(英語)

http://search.cpan.org/~conklin/MIDI-Perl-0.82/lib/MIDI.pm

そして、Perl/CGIプログラミングをする上で、自分の目的に合ったモジュールを使えるようにしておくといろいろ便利です。

Perl/CGIプログラムの開発時間を短縮できるだけでなく、煩雑な部分はほとんどモジュールがやってくれるので、プログラマーはソフトウェア開発に集中できるのです。

さらに、前述のCPANに公開されているモジュールは、世界中のPerl/CGIプログラマーの中でも上級者が作成したものが多いです。

なので、Perl/CGIのソースコード(プログラムコード)を読む(トレース)するだけでもかなり勉強になります。

せっかく無料でこれだけのものが公開されているわけですから、ぜひあなたのPerl/CGIプログラム学習やソフトウェア開発に役立ててください。

それでは今回の学習を始めましょう。

CGIモジュールの概要

まずは、CGI.pmモジュールまたはCGIモジュールの概要から学習していきましょう。

CGIモジュールとは

CGI.pmモジュールまたはCGIモジュールとは、 Perl/CGIプログラミングをする 上で、基本的な入力および出力部分を提供しているモジュール群のことです。

以前、 Perlの関数ライブラリ というPerlプログラミング解説で、 関数(サブルーチン) を別のファイルに設置し、必要に応じて呼び出すといったことを学習しましたね。

Perl/CGIに限らずプログラムというのは、細かい処理ごとの関数をつなぎ合わせることでひとつのソフトウェアを構成しています。

ということは、関数ごとに分けられたプログラムというのはプログラミング言語さえ同じであれば、別のソフトウェアに組み込んでも使用できるということです。

例えば、前回の Perl/CGIプログラミング学習 HTMLフォームから入力されたデータを、Perl/CGIプログラム側で受け取るGetParameter関数 というのを作成しましたよね。

このGetParameter関数はPerl/CGIプログラムを作成する際、何らかのデータ入力を受け付けるプロセスを用意する場面で、何度でもコピーして使い回しができるということです。

大雑把に言ってしまえば、ここで学習するCGIモジュールというのも、この感覚に似ています。

「CGIモジュールでPerl/CGIプログラミングに役に立つ関数を提供しますから、みなさんは使いたい機能だけ呼び出して使ってくださいね。」

的な感じです。

さらに、CGI.pmまたはCGIモジュールというのは、無料で一般公開されています。

CGIモジュールのダウンロードページはこちら(英語)。

http://search.cpan.org/~lds/CGI.pm-3.50/lib/CGI.pm

Perl/CGIプログラムがわかる方であれば、自由にダウンロードして開発中のPerl/CGIプログラムに組み込むことができます。

一般的なレンタルサーバーを使用していれば、ここでCGIモジュールをダウンロードしなくてもすでにインターネットサーバーにインストールされている場合が多いです。

なのでわざわざ上記のCGIモジュールのダウンロードページに行って、英語と格闘する必要はありません。

詳しくは後述しますが、すでにCGIモジュールがインストールされているサーバーであれば、Perl/CGIプログラム内でCGIモジュールを宣言するだけで使えます。

このように、Perl/CGIプログラミングに便利な関数が無料で提供されているということから、たくさんのPerl/CGIプログラマーがCGIモジュールを使用しています。

そして、たくさんのPerlプログラマーに使用されているということは、プログラムとしての完成度がある程度高いという証明でもあります。

仮にCGIモジュールにバグがあったとしても、利用者数が多ければ誰かが修正してバージョンアップが行われることでしょう。

もちろん、今回説明するようなCGIモジュールまたは、CGI.pmモジュールを使用しなくても、同じような機能を持ったPerl/CGIプログラムを作成することは可能です。

しかし、CGIモジュール以外にもいえることですが、既存のものを使用した方がPerl/CGIプログラム開発にかかるコストを抑えることができます。

Perl/CGIプログラム開発にかかる時間はもちろん、プログラマーの労力や人件費など、メリットが大きいです。

なので今回学習するCGIモジュールはもちろん、上記のCPANで公開されているいくつかのモジュールは使えるようにしておくと便利です。

CGIモジュールの機能

ここでは、CGIモジュールまたは、CGI.pmモジュールが提供している関数について説明します。

CGIモジュールまたは、CGI.pmモジュールは、主に Perl/CGIプログラムへのデータ入力 と、 HTML の出力部分を提供している関数群です。

つまり、Perl/CGIプログラムでのデータの受け取り部分と、HTML表示に関与しているということです。

データ入力(Perl/CGIプログラム側でデータを受け取る)処理は…

テキスト入力、ラジオボタン、チェックボックス、セレクトボックスやファイルアップロードなど各種 HTMLフォーム からの入力に対応しています。

さらに、GET送信のようにパラメータ形式のデータを受け取ることもできます。

データ出力処理は…

HTTPヘッダー(HTMLデータ出力の前にサーバー側で付加する文字コード情報などのこと)や、HTMLのheadタグ、bodyタグ、formタグなども出力できます。

もちろん出力するHTMLの文字コードやタイトル、各種フォームタグ内の要素などは自由にカスタマイズできます。

CGIモジュールの機能については以上です。

ほとんどのPerl/CGIプログラムが、何らかのデータを受け取り、何らかの結果を出力しています。

このことから本当にたくさんのPerl/CGIプログラムが、CGIモジュールまたは、CGI.pmモジュールを利用しているということが想像できるかと思います。

それでは、CGIモジュールまたは、CGI.pmモジュールの使い方を説明していきます。

CGIモジュールでデータの取得

まずは、CGIモジュールまたは、CGI.pmモジュールを使って、Perl/CGIプログラムに渡されたデータを取得する方法について学習していきましょう。

Perl/CGIプログラムへの入力処理の基礎 については、前回やったのでそちらを参照してください。

通常入力

CGIモジュールを使用して、 HTMLフォーム からサイト訪問者が入力したデータを受け取るためには、「param」という命令文を使用します。

HTMLフォーム

以下のような氏名とメールアドレスを入力するような HTMLフォーム があったとします。

<html lang="ja">
<head>
<META http-equiv="Content-Type" content="text/html; charset=shift_jis">
<title>Form Sample</title>
</head>
<body>
<div align="center">
<h1>Form Sample</h1>
<form action="sample.cgi" method="post">
<table border="0" cellspacing="5" cellpadding="5">
<tr valign="top" align="left">
<td>NAME</td>
<td><input type="text" name="name" size="30"></td>
</tr>
<tr valign="top" align="left">
<td>EMAIL</td>
<td><input type="text" name="email" size="30"></td>
</tr>
<tr valign="top" align="left">
<td></td>
<td><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</div>
</body>
</html>

この HTMLファイルを表示 させると、氏名とメールアドレスを入力するフォームが現れます。

そして、氏名とメールアドレス入力欄にそれぞれ情報を入力しOKボタンを押すと、CGIにPOST形式で各種データが送られます。

ちなみに、GET送信でCGIにパラメータをわたしたいときには、POST部分を、GETに変更するだけでGET送信を実現できます。

Perl/CGIサンプル(1)

上記サンプルHTMLフォームから氏名とメールアドレスを受け取る「sample.cgi」には、以下のように記述します。

#!/usr/bin/perl

use strict;
use CGI;

my $cgi = new CGI;

my $name = $cgi->param('name');
my $email = $cgi->param('email');

print "Content-Type: text/html; charset=shift_jis\n\n";
print "Name: $name<br>\n";
print "Email: $email<br>\n";
exit;

変数 「$name」には、入力された氏名が格納されます。

変数「$email」には、入力されたメールアドレスが格納されます。

もちろんどちらもデコード処理済みです。

Perl/CGIサンプル(2)

一つ一つデータを受け取るのが面倒だという方は、まとめてデータを受け取ることもできます。

#!/usr/bin/perl

use strict;
use CGI;

my $cgi = new CGI;
my @params = $cgi->param();
my %input;

foreach (@params) {
$input{$_} = $cgi->param($_);
}

print "Content-Type: text/html; charset=shift_jis\n\n";
foreach my $key (keys %input) {
print "$key : $input{$key}<br>\n";
}
exit;

入力されたデータを、「%input」という 連想配列 に格納しています。

ここではまとめて foreachループ を使っていますが、個別のデータを呼び出すには連想配列の添え字に、フォームで指定した名前を使用します。

例えば、「$input{'email'}」とすれば、入力されたメールアドレスを呼び出せます。

Perl/CGIサンプル(3)

他にも、こんな感じにプログラミングすることもできます。

#!/usr/bin/perl

use strict;
use CGI;

my $cgi = new CGI;
my %input;

for ($cgi->param) {
$input{$_} = $cgi->param($_);
}

print "Content-Type: text/html; charset=shift_jis\n\n";
foreach my $key (keys %input) {
print "$key : $input{$key}<br>\n";
}
exit;

結果としては、同じ 連想配列 「%input」に入力データを格納します。

どちらのPerl/CGIプログラムも、CGIモジュールのparam関数を使って入力データをループ処理で連想配列に格納させています。

このあたりの処理は、Perl/CGIスクリプトを開発する上では定番処理部分ですので、自分なりにいろいろな形を試してあなたのスタイルを作り出してください。

これだけでもCGIモジュールは便利ですよね。

前回は、 CGIモジュールを使わずに入力データを受け取るPerl/CGIプログラムを作成 しましたが、こんなにスマートにはいきませんでしたよね。

ファイルアップロード

CGIモジュールを使ってアップロードされたファイルを受け取ることができます。

HTMLのファイルアップロードフォーム で指定されたファイルパスを、Perl/CGIプログラムで受け取りインターネットサーバーに保存します。

HTMLフォーム

まずは、 ファイルをアップロードするHTMLフォームサンプル を作成します。

<html lang="ja">
<head>
<META http-equiv="Content-Type" content="text/html; charset=shift_jis">
<title>Form Sample</title>
</head>
<body>
<div align="center">
<h1>Form Sample</h1>
<form action="sample.cgi" method="post" enctype="multipart/form-data">
<table border="0" cellspacing="5" cellpadding="5">
<tr valign="middle" align="left">
<td>File</td>
<td><input type="file" name="filepath" size="20"></td>
<td><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</div>
</body>
</html>

ファイルをアップロードするためのHTMLフォームの場合は、formタグに「enctype="multipart/form-data"」を追加します。

そしてファイルアップロード用の入力欄を作成するところのinputタイプには、fileを指定します。

詳しくは、 HTMLのformタグ を参照してください。

Perl/CGIサンプル(1)

前述のHTMLフォームよりアップロード指定されたファイルを、Perl/CGIプログラム側で受け取る方法について解説します。

CGIモジュールでは前述の通常入力と同じようにparam関数を使います。

すると、アップロードするファイルパスを得ることができるので、それをファイルハンドルにしてファイルを読み込んでいきます。

#!/usr/bin/perl

use strict;
use CGI;

my $cgi = new CGI;
my $buffer;
my $filepath = $cgi->param('filepath');
my @part = split(/[\\\/]/,$filepath);
my $filename = pop @part;
my $message;

if (open(OUT,">$filename")) {
binmode OUT;
while (read($filepath, $buffer, 1024)) {
print OUT $buffer;
}
close OUT;
$message = "<a href=\"$filename\">$filename</a>";
} else {
$message = "Can't open $filename";
}

print "Content-type: text/html\n\n";
print $message;
exit;

変数 「$filepath」にはアップロードされるファイルまでのパスが格納されています。

このパスをファイルハンドルにして、read関数で1024バイトずつ変数「$buffer」に読み込んでサーバー側に保存していきます。

全てのデータを保存できたら whileループ 終了です。

これで HTMLフォーム からファイルアップロード指定を受けたファイルを、 Perl/CGIプログラム 側で受け取って、サーバーに保存するところまではできました。

しかし、これではまだ実際のPerl/CGIプログラムとしては使えません。

なぜなら、アップロードされたファイルが大きすぎたり、そもそもファイルアップロードの指定なしにフォーム送信が行われる場合も考慮する必要があるからです。

さらに、サーバーに保存するファイル名には日本語は使えないので、そのあたりをどうしていくのかということも考えなくてはいけません。

Perl/CGIプログラムサンプルでは、ファイルアップロード用のパスを split関数 を使って「\」または「/」を基準に分解し、そこからファイル名を取り出し何も手を加えずそのまま使っています。

もしファイル名に日本語や、拡張子が「cgi」や「pl」などインターネットサーバーにとって特別な意味を持つものだった場合、エラーが発生します。

今回はアップロードされるファイルが何かわからない状態なのでこのようにしました。

通常、ファイルアップロードを促す場合、画像や文書などある程度ファイルの種類は絞られるので、もう少しましなコーディングができるかと思います。

ぶっちゃけサーバーに保存するファイル名については、Perl/CGIプログラマーの工夫次第で何とかなるものです。

なので次は、前述のアップロードファイルの問題について、その対処法を学習していきましょう。

Perl/CGIサンプル(2)

前述のHTMLフォームで、ユーザーは必ずアップロードファイルを指定してくれるとは限りませんよね。

アップロードファイルを何も指定しないで送信する場合もあるわけです。

ここではそんな場面を想定して、Perl/CGIプログラムを作成していくことにします。

アップロードファイルの有無はCGIモジュールを使うと簡単にチェックできます。

CGIモジュールのバージョン2.47以降では、uploadというメソッドがあるのでそれを使ってアップロードファイルの有無を判定します。

#!/usr/bin/perl

use strict;
use CGI;

my $cgi = new CGI;
my $buffer;
my $filehandle = $cgi->upload('filepath');
my $filepath = $cgi->param('filepath');
my @part = split(/[\\\/]/,$filepath);
my $filename = pop @part;
my $message;

if (!$filehandle) {
$message = 'Input Error !';
} elsif (open(OUT,">$filename")) {
binmode OUT;
while (read($filehandle, $buffer, 1024)) {
print OUT $buffer;
}
close OUT;
$message = "<a href=\"$filename\">$filename</a>";
} else {
$message = "Can't open $filename";
}

print "Content-type: text/html\n\n";
print $message;
exit;

CGIモジュールのuploadメソッドは、何も問題なければファイルハンドルを、何らかの問題があるときにはundefを返します。

なのでアップロードするファイルがあるときには、 変数 「$filehandle」にはファイルハンドルが格納されます。

アップロードするファイルがないときや、何らかの問題があったときには変数「$filehandle」には、何も格納されません。

上記サンプルのように if文 などを使って変数「$filehandle」を参照すれば、Perl/CGIプログラム側でファイルアップロード処理をするのかしないのかを判定することができます。

Perl/CGIプログラム側でファイルアップロード(ファイル読み込み)処理を行う場合、当然「$filehandle」にはファイルハンドルが格納されています。

なので、read関数のファイルハンドル(第一引数)として使うのが望ましいです。

Perl/CGIサンプル(3)

次は、HTMLフォームからアップロードされたファイルの容量が大きすぎた場合の処理について学習していきましょう。

HTMLのファイルアップロードフォームよりPerl/CGIに送られたファイルは、プログラム内部ではCGIモジュールを経由します。

なので、あらかじめCGIモジュールに受け取ることのできる最大のファイルサイズを指定しておき、アップロードされるファイルがそれを上回る場合はエラーを出力するようにするのがよいでしょ う。

#!/usr/bin/perl

use strict;
use CGI;

$CGI::POST_MAX = 1024 * 1024;

my $cgi = new CGI;
my $message;

$cgi->param;

if ($cgi->cgi_error) {

$message = $cgi->cgi_error;

} else {

my $buffer;
my $filehandle = $cgi->upload('filepath');
my $filepath = $cgi->param('filepath');
my @part = split(/[\\\/]/,$filepath);
my $filename = pop @part;

if (!$filehandle) {
$message = 'Input Error !';
} elsif (open(OUT,">$filename")) {
binmode OUT;
while (read($filehandle, $buffer, 1024)) {
print OUT $buffer;
}
close OUT;
$message = "<a href=\"$filename\">$filename</a>";
} else {
$message = "Can't open $filename";
}

}

print "Content-type: text/html\n\n";
print $message;
exit;

まず、「$CGI::POST_MAX」にアップロードされるファイルの最大サイズを指定しておきます。

Perl/CGIプログラムサンプルでは、1024バイト×1024バイトで1メガバイトを指定しています。

次に、「$cgi->param;」とすることにより、Perl/CGIプログラムがアップロードファイルを受け取る準備をCGIモジュールに促します。

この段階で1メガバイト以上のファイルがアップロードされそうな場合や、アップロードが中断されたなどのトラブルがあった場合は、CGIモジュール内でエラーとなります。

CGIモジュール内での処理で何らかのエラーが発生したときには、「$cgi->cgi_error」を呼び出すことによりエラーメッセージを受け取ることができます。

何も問題ない場合には、最初の if文 は不成立となり、アップロードファイルを受け取る処理に移るというわけです。

これで、CGIモジュールを使ったファイルアップロードプログラミングについては以上です。

CGIモジュールでHTML出力

CGIモジュールまたは、CGI.pmモジュールを使用して、 HTMLタグ を出力させる方法について学習していきましょう。

CGIモジュールには、HTTPヘッダーを出力する「header」。
HTMLヘッダーとbodyタグを出力する「start_html」。
HTMLフッターを出力する「end_html」。

というメソッドがあります。

これらを使うと本文部分以外はほとんど手をくわえずに、Perl/CGIが出力しなくてはいけないデータをそろえることができます。

例えば次のようにすれば、 Perl/CGIプログラム から簡単にHTMLタグを出力できます。

#!/usr/bin/perl

use CGI;

my $cgi = new CGI;

print $cgi->header;
print $cgi->start_html(-title => "CGI Sample Script !");
print "Test Script !";
print $cgi->end_html;
exit;

このPerl/CGIスクリプトを、「sample.cgi」という名前でテキスト形式で保存し、FTPでサーバーにアップし、適切なパーミッションを与えます。

その後、インターネットエクスプローラなどのウェブブラウザでアクセスすると「Test Script !」と表示されます。

もちろん、HTMLソースものぞけるので興味のある方はぜひのぞいておいてください。

CGIモジュールを使ったHTML出力詳細設定

前述のPerl/CGIスクリプトでもよいのですが、このままでは言語の指定や文字コードの指定がないので、画面出力時に 文字化け などを起こす可能性があります。

文字化けなどを避けるために、もう少しCGIモジュールに詳細情報を指定して画面出力させてみましょう。

#!/usr/bin/perl

use CGI;

my $cgi = new CGI;

print $cgi->header(
-type=>'text/html; charset=euc-jp'
);
print $cgi->start_html(
-title => "CGI Sample Script !",
-charset => 'EUC-JP',
-encoding => 'EUC-JP',
-lang => 'ja'
);
print "Test Script !";
print $cgi->end_html;
exit;

このPerl/CGIプログラムでは、HTTPヘッダーと METAタグ 部分の文字コードセットの指定をEUCコード「EUC-JP」に指定しています。

シフトジスコードを指定したいときは「SHIFT_JIS」を、UTF-8コードを指定したいときは「UTF-8」と記述します。

CGIモジュールを使用した入力フォームの出力

CGIモジュールには、 HTML入力フォームの作成 を助けるメソッドがあります。

代表的なところでは…

フォーム開始タグを生成する「start_form」。
テキストフィールドを生成する「textfield」。
フォーム終了タグを生成する「end_form」。

などがあり、当然これらはPerl/CGIプログラムの動作環境に合わせてカスタマイズできるようになっています。

例えば、formタグにはaction指定が必要ですよね。

actionに指定された 絶対パスまたは相対パス に、フォーム入力データが送られるわけですからほぼ必須なんですが、これはPerl/CGIプログラムの動作環境によって明らかに変化するところです。

さらに、各inputタイプや、そこに属するnameなど、自由に指定したいものばかりです。

CGIモジュールはそんな条件下でも使うことができます。

例えばこんな感じでプログラミングしていきます。

#!/usr/bin/perl

use CGI;

my $cgi = new CGI;

print $cgi->header(
-type=>'text/html; charset=euc-jp'
);
print $cgi->start_html(
-title => "CGI Sample Script !",
-charset => 'EUC-JP',
-encoding => 'EUC-JP',
-lang => 'ja'
);
print $cgi->start_form(
-action => $cgi->url,
-method => 'POST'
);
print 'Email', $cgi->textfield(
-name => 'email',
-size => 40
);
print $cgi->submit(value => 'Submit');
print $cgi->reset(value => 'Reset');
print $cgi->end_form;
print $cgi->end_html;
exit;

このPerl/CGIスクリプトを実行すると、Emailの入力フォームを表示します。

「$cgi->url」というのは、このPerl/CGIファイルまでのURLを返します。

CGIモジュールでリダイレクト

リダイレクトとは、アクセスしてきたインターネットエクスプローラなどのウェブブラウザを、こちらの指定したURLに遷移させる機能のことです。

HTMLのMETAタグ にもこれと似たような機能がありましたね。

CGIモジュールにも、リダイレクト用のriderectというメソッドがあります。

リダイレクトをしたい時には、次のようにします。

#!/usr/bin/perl

use CGI;

my $cgi = new CGI;

print $cgi->redirect('https://kimurashuuichi.com');
exit;

このPerl/CGIファイルにアクセスすると、このサイトのトップページにジャンプします。

内部的には次のような出力になります。

Status: 302 Moved
Location: https://kimurashuuichi.com

Statusはデフォルトで「302 Moved」です。

この302というのは一時的なリダイレクト(Moved Temporary)をあらわすものです。

もし、例えば恒久的なリソースの移動(Moved Permanenty)を示す301を返したいという場合は次のようにします。

#!/usr/bin/perl

use CGI;

my $cgi = new CGI;

print $cgi->redirect(
-url => 'https://kimurashuuichi.com',
-status => '301 Moved'
);
exit;

「-url」は代わりに「-location」や「-uri」でもかまいません。

「-url」や「-status」以外には、「-target(ターゲット・ウィンドウ)」「-cookie(クッキー)」などを指定することができます。

CGIモジュールについてのPerl/CGIプログラミング学習は以上です。



編集後記

今回学習しましたCGIモジュールは、Perl/CGIプログラミングを行ううえで使いこなせるとかなり便利です。

ぜひ覚えておいてください。

本編でCGIモジュールを学習したので、編集後記ではそれに関連して、CGI::Carpモジュールを紹介します。

このCGI::Carpモジュールは、Perl/CGIプログラム中でエラー処理する際、使用するdie関数をサポートするプログラムです。

die関数は、Perl/CGIプログラムを強制終了してくれるので、便利に使っている人も多いかと思います。

しかし、die関数の引数であるエラーメッセージは、通常サーバー側にのみ出力されるもので、ウェブブラウザから確認することはできません。

そこでCGI::Carpモジュールを宣言しておくことにより、die関数の引数であるエラーメッセージをウェブブラウザにも表示させることができます。

こんな感じでプログラミングします。

#!/usr/bin/perl

use CGI::Carp qw(fatalsToBrowser);

die 'Error Message !';

このPerl/CGIプログラムを実行すると、エラー画面に「Error Message !」と表示されます。

これでPerl/CGIプログラム内でdie関数を使っても、どの部分で止まったのかをウェブブラウザから確認することができます。

今回の学習は以上です。

ありがとうございました。

<戻る>