Perl Perl_2
Perl 「 モジュール 」 CGI 実用的なファイルアップロード 16 書き込み処理 詳細 (0x11d)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その 0x11d 回。
CGI.pm で、実用的なファイルアップロードの処理を確認する。その 15 回。データ書き込み処理の詳細を確認する。
ファイルアップロードのためのコード
ファイルアップロードのためのプログラムコードを改めて確認します。原理的には (0x109) の基本処理と変わりありません。
# 書き込みモードでファイルハンドルをオープン
open ( OUTFILE, ">", "$file" ) or die "Couldn't open $file for writing: $!";
# 関数 read() でファイルを読み込み
while ( $bytesread = read( $filename, $buffer, $num_bytes )) {
# $totalbytes = $totalbytes + $bytesread
$totalbytes += $bytesread;
# バッファの内容を書き込み
print OUTEFILE $buffer;
}
# もし $bytesread の値が undef なら die
die "Read failure" unless defined( $bytesread );
# もし $totalbytes の値が undef なら
unless ( defined ( $totalbytes )) {
# エラーメッセージ
print "<p>Error: Could not read file ${untainted_finename},";
print "or the file was zero length.";
} else {
# $totalbytes の値が undef でなければ完了のメッセージ
print "<p>Done. File $filename uploaded to $file ($totalbytes bytes)";
}
# ファイルハンドルをクローズ
close OUTFILE or die "Couldn't close $file: $!";
今回は上記の処理をひとつずつ確認します。
書き込みモードでファイルハンドルをオープン
まず、ファイルハンドルを、書き込みモードでオープンします。ここでは (0x44) で確認した 3 引数の open 演算子を利用しています。これは、旧来型の 2 引数の open に比較して安全です。
書き込みモードには「 > 」を利用しているため、同名のファイルがあった場合は上書きします。既存のファイルに追加で書き込みたい場合は、「 >> 」を利用します。これらの機能は、シェルの「 リダイレクション 」( リダイレクト ) 機能に準拠しています。
open ( OUTFILE, ">", "$file" ) or die "Couldn't open $file for writing: $!";
関数 die のメッセージに含まれる「 $! 」は、(0x48) で確認した通り、システムからのメッセージが格納されています。
ファイルの読み込みと書き込み
関数「 read() 」を利用したファイルの読み込みと、ファイルハンドルを利用したデータの書き込みを行います。
(0x118) で予め "宣言 / 初期化" しておいた変数を利用します。基本的な処理は (0x109) と変わりありません。
while ( $bytesread = read( $filename, $buffer, $num_bytes )) {
$totalbytes += $bytesread;
print OUTEFILE $buffer;
}
基本的な処理と異なる部分は、関数 read() が返す値 ( $num_bytes のバイト数 ) を、1 ループ毎に累算していることです。
これによって、ループが終了 ( ファイルの読み込みが完了 ) した際に、合計読み込みバイト数、つまり、読み込んだファイルのデータサイズが分かります。データサイズを格納したスカラ変数「 $totalbytes 」は、アップロード処理完了時のメッセージに利用します。
累算に利用している「 二項代入演算子 」( binary assignment operator) は、(0x86) で確認しました。
読み込みエラーの検知
読み込みんだファイルのバイト数を逐次格納するスカラ変数「 $bytesread 」を関数「 define 」でチェックして、データの読み込みエラーを検知しています。
もし、$bytesread が「 undef 」だった場合は、関数 die によってプログラムを終了しつつ「 Read failure 」( 読み込み失敗 ) のメッセージを表示します。
die "Read failure" unless defined( $bytesread );
このチェックは「 define 」を使っているのがポイントです。なぜなら、「 define 」は、未定義値「 undef 」を確認した場合のみ 偽 を返すからです。
つまり、通常の条件式では 偽 が返される「 空文字 」や数値「 0 」が対象であっても「 define 」を使えば 真 を返します。
実際に、ここで検査されている変数 $bytesread は、正常にデータを読み込んでいた場合でも、最終的には値「 0 」を持っているはずです。
それによって、while 文の条件式を抜けてきているので ( (0x109) )、式修飾子 ( (0x82) ) unless で異常をチェックするには、関数「 define 」が相応しいという訳です。
仮に、$bytesread の値が「 undef 」だった場合は、もとから何もデータを読み込めていないことを意味するので、それはエラーと判断出来ます。
関数「 define 」は、(0x1a) で確認しました。
読み込みエラーの検知 その 2
続いてもうひとつ、エラー検知のための記述があります。ここではファイルサイズを格納する変数 $totalbytes の値が「 undef 」だった場合をチェックしています。
unless ( defined ( $totalbytes )) {
print "<p>Error: Could not read file ${untainted_finename},";
print "or the file was zero length.";
変数 $totalbytes は、上述した通り、読み込んだデータの合計バイト数を持っています。
変数 $bytesread が undef ではない ( ひとつ前のチェックを通過した ) 状況で、どのようにすれば $totalbytes が undef になりえるのかは、今の僕では分かりませんが、現時点では多角的なチェックということで理解します。
仮にここで undef を確認した場合は、「 Could not read file, or the file was zero length. 」( ファイルが読み取れませんでした, または ファイルの長さがゼロです. ) と言うメッセージを表示します。
ここでは関数 die によるプログラムの強制終了はしません。何故なら、続く処理はファイルハンドルのクローズのみだからだと理解しています。
データ読み込みに問題がなかった場合
変数 $totalbytes を検査しても問題がない場合は、次の様にして、メッセージ「 ファイルのアップロードが完了した 」を表示します。
} else {
print "<p>Done. File $filename uploaded to $file ($totalbytes bytes)";
}
変数「 $filename 」で元のファイル名を、変数「 $file 」でアップロード後のファイル名を、変数「 $totalbytes 」によって、ファイルサイズを表示しています。
ちょっとした親切ですね。
ファイルハンドルをクローズする
最後の処理は、ファイルハンドルのクローズです。正常にクローズ出来なかった場合に備えて、関数 die をセットで記述しています。
close OUTFILE or die "Couldn't close $file: $!";
0x11d -> 0x11e へ
次回は、Cgi upload - perlmeme.org の例示をローカライズしつつ、全体のコードとその動作を確認します。
参考情報は書籍「 初めての 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)