Perl Perl_2
Perl 「 モジュール 」 CGI 実用的なファイルアップロード 11 アップロードの処理 破 (0x118)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その 0x118 回。
CGI.pm で、実用的なファイルアップロードの処理を確認する。その 11 回。実際のアップロードの処理を請け負うサブルーチンの中身を確認する。
ファイルのアップロード処理
Cgi upload - perlmeme.org で紹介されている、実用的なコードでは、次の様にしてサブルーチン「 save_fiel() 」にファイルアップロードのための処理を任せています。
if ($q->param()) {
save_file($q);
}
print $q->end_html;
exit 0;
今回は、このサブルーチンの冒頭部分を確認します。
変数の初期化
サブルーチンは、次の記述から始まります。
sub save_file($) {
# 引数の CGI オブジェクトのデータを取得
my ($q) = @_;
# 関数 read() のための変数
my ($bytesread, $buffer);
# 関数 read() で一度に読み込むバイト数を設定
my $num_bytes = 1024;
# ファイルの合計バイト数を格納する変数
my $totalbytes;
# ファイル名と同名のファイルハンドルを取得
my $filename = $q->upload('filename');
# 汚染チェックを通過したファイル名を格納する変数
my $untainted_filename;
ここでは、ファイルアップロードの処理に利用する各種変数を宣言/初期化してしています。
これらの変数は、いくつかを除いて (0x109) で確認した基本処理と同じ変数です。
プライベートな配列とレキシカル変数
基本処理との違う部分を挙げると、まずは、次の変数があります。
my ($q) = @_;
ここでは、引数で受け取った CGI オブジェクトの値を、特別な配列「 @_ 」を介して、レキシカル変数「 $q 」が引き受けているところです。
変数 $q が「 ( ) 」によってリストコンテキストとされているのは、CGI オブジェクトを引き受けることと関係があるはずですが、現時点では明確な理解は出来ていません。
特別な配列「 @_」と、「 my 」演算子を使った「 レキシカル変数 」は、(0x33) で確認しました。
トータルバイト数
次の変数も、基本処理では利用していない変数です。
my $totalbytes;
スカラ変数「 $totalbytes 」は、その名前の通り、バイト数の合計を格納します。
この変数は、別途宣言してあるスカラ変数「 $bytesread 」が、関数「 read() 」の処理に伴って一時的に格納したバイト数の値を合計するための変数です。
このバイト数の合計は、ファイル読み込み時のエラー検知や、アップロード処理完了後のアップロードサイズ表記に利用します。
メソッド upload()
次の部分は、基本処理ではメソッド「 param() 」を利用していた部分です。
my $filename = $q->upload('filename');
メソッド param() とメソッド upload() の機能の違いは、(0x109) の時点では良く理解出来ていませんでしたが、perldoc.jp を読み進めるとその解説がありました。
"ファイルアップロードを処理するための古い方法" によれば、元々ファイルアップロードのためのファイルハンドルは、メソッド param() によって生成していた様です。
しかしながら、use strict を利用する昨今の慣習 ( (0x35) ) においてこれが問題になりました。
なぜならば、メソッド param() が返す値は、ただの「 文字列 」と「 ファイルハンドル 」の 2 つの性質を兼ね備えていますが、use strict プラグマは、文字列をファイルハンドルとして利用する場合に警告を出すように出来ているからです。
メソッド upload() は、メソッド param() の特徴による弊害をクリアにするために追加されたメソッドで、常にファイルハンドルのみを返す様に出来ています。
ですから、これから書くことになるファイルアップロードのためのプログラムでは、基本的にメソッド upload() を使うことが推奨されます。
ただし、古いアプリケーションプログラムとの連携を考えた場合は、メソッド upload() の利用に注意するべき点があると言います。
それは、upload() によって得られる "軽量の" ファイルハンドルが、モジュール IO::Handle の派生クラスではないことから、他のモジュールとの相互運用に支障が出る可能性があるということです ( param() で得るファイルハンドルは互換性があるということ ? )。
ですから、perldoc の例示では、次の様にメソッド handle() を利用した問題の解決方法を示しています。
$lightweight_fh = $q->upload('field_name');
# undef may be returned if it's not a valid file handle
# ( undef が返されることがあります もしそれが 有効でないファイルハンドルなら )
if (defined $lightweight_fh) {
# Upgrade the handle to one compatible with IO::Handle:
# ( ハンドルをアップグレード 互換性のあるものに IO::Handle と )
my $io_handle = $lightweight_fh->handle;
open (OUTFILE,'>>','/usr/local/web/users/feedback');
while ($bytesread = $io_handle->read($buffer,1024)) {
print OUTFILE $buffer;
}
}
この方法では、「 最初に呼び出したときに IO::Handle を読み込む 」とありますが、メソッド handle() は use IO::Handle を宣言することなく利用可能です。
ちなみに、メソッド handle() がどのモジュールに属するメソッドなのかは現時点で確認出来ていません。
Note: この方法は、古いプログラムとの互換性維持に役立つらしい。ということは新しいプログラムのみの場合 IO::Handle との互換ファイルハンドルは不要 !?
汚染チェック後のファイル名
実用的なファイルアップロードでは、入力された値 ( ファイル名 ) に対していくつかの汚染チェックを行います。次のスカラ変数は、この汚染チェックを経た値を格納し、サーバ上に保存する際のファイル名とします。
my $untainted_filename;
0x118 -> 0x119 へ
次回は、ファイル名の汚染チェックの方法を確認します。
参考情報は書籍「 初めての Perl 第 6 版 」を中心に perldoc, Wikipedia および各 Web サイト。それと詳しい先輩。
目次 - Perl Index
Perl mp2 翻訳 Web コンテンツ圧縮の FAQ (d228)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)