Perl:入力フォーム(POST/GET)からの受け取り

 Perlでよく使用する入力フォーム(POSTやGET)から値を受け取る時の処理を調べてみました。

※プログラム初心者のため、このページのプログラムソースは正常に動作しない事がありますご注意下さい。
(あっちこっちのサイトの情報を寄せ集めてるので思わぬ不具合があるかも)

 POST・GET 両方とも取得可能(同時は無理・排他)でファイルも扱えます。
 GETまたはPOSTのデータを受け取ると、変数 $form{“*”} (*は送信時に使ったnameの値)に格納されます。

 技術不足でファイルのアップロードには色々制限があります。
 ・ファイルをアップロードする時は、formに「method=”post” ENCTYPE=”multipart/form-data”」を記載が必要(ファイルが無い時は不要)
 ・アップロードする inputタグは <input type=”file” name=”FILE?”> (name値はFILEで始まる事?部分は任意)
 ・ファイル以外のinputタグには name=”FILE?” は使用禁止!
 ・ファイルは変数 $formfile{“FILE?”}にファイル本体、$form{“FILE?”}ファイル名が格納されます。(*は送信時に使ったもの)、$formfileをbinmodeでサーバー上に書き出せばアップロード完了です。

 注意:textareタグで、Windowsから送られた改行コードは¥r¥nとなっているので、そのままtextareに出力すると2重改行になります「$str =~ s/\r\n/\n/g;」などで改行コードを処理して下さい。

修正履歴
2013/09/01:デコード処理「$value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack(“C”, hex($1) )/eg;」が遅いので変更
2013/12/23:正しくアップロード出来ない事があったのを修正(境界値の取得ミス)

my(%form,%formfile);
my($input,$data,$name,$value,%count,$flag);
my($boundary,$eind,$len,$buffer,@datas,$dat,$keyid1,$keyid2,$keyid3,$keyid4,$rname,$rvalue,$fname,@filevals);
#POST/GET値取得
if ($ENV{'REQUEST_METHOD'} eq "POST"){
	if ($ENV{'CONTENT_TYPE'} =~ m/multipart\/form-data/){	#POSTバイナリ
		#境界値をCONTENT_TYPEから読み出し
		($boundary) = $ENV{'CONTENT_TYPE'} =~ /boundary="([^"]+)"/;
		 unless($boundary) { ($boundary) = $ENV{'CONTENT_TYPE'} =~ /boundary=(\S+)/;}
		 $eind=index($boundary,',');
		if ($eind>0){$boundary = substr($boundary,0,$eind);}
		$boundary = "--".$boundary;
		#データ取得
		binmode(STDIN);
		read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'});
		#分割
		@datas=split(/$boundary/,$buffer);
		foreach $dat(@datas){
			if ($dat =~ /Content-Disposition: form-data; name/i){
				$keyid1=index($dat,'name');
				$keyid2=index($dat,'"',$keyid1+6);
				$rname=substr($dat,$keyid1+6,$keyid2-$keyid1-6);
				$rvalue=substr($dat,$keyid2+2);
				#フォーム名にFILEがある場合はアップロードとして処理
				#※逆にFILEがあるとアップロードとして処理されるのでフォーム作成時は注意
				if (substr($rname,0,4) eq 'FILE'){			#ファイル
					#不要なデータを除く
					$keyid3=index($rvalue,'filename="');
					$keyid4=index($rvalue,'"',$keyid3+10);
					$fname=substr($rvalue,$keyid3+10,$keyid4-$keyid3-10);
					$fname=~ s/\n//ig;
					$fname=~ s/\r//ig;
					@filevals=split(/\r\n\r\n/,$rvalue);
					chop $filevals[1];
					chop $filevals[1];
					$fvalue=$filevals[1];
					$form{$rname}=$fname;
					$formfile{$rname}=$fvalue; 
				}else{										#ファイル外
					#末尾のCR/LFコードを削除
					while((substr($rvalue,-1,1) =~ /\r/) || (substr($rvalue,-1,1) =~ /\n/)){
						chop $rvalue;
					}
					#先頭のCR/LFコードを削除
					while((substr($rvalue,0,1) =~ /\r/) || (substr($rvalue,0,1) =~ /\n/)){
						$rvalue=substr($rvalue,1);
					}
					$form{$rname}=$rvalue; 
				}
			}
		}
		$flag=1;	#return(1);
	}else{													#POSTテキスト
		read(STDIN, $input, $ENV{'CONTENT_LENGTH'});
		$flag=2;
	}
}else{														#GETテキスト
	$input = $ENV{'QUERY_STRING'};
	$flag=2;
}
#データ分割
if ($flag==2){
	foreach $data (split(/&/, $input)) {
		($name, $value) = split(/=/, $data);
		#URLデコード
		$value =~ tr/+/ /;
		$value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
		#登録
		$count{"$name"}=$count{"$name"}+1;
		if ($count{"$name"} > 1){
			$form{"$name"} = $form{"$name"}."\0".$value;
		}else{
			$form{"$name"} = $value;
		}
	}
}


使い方がわかりにくいので次のプログラムをひっつけて実行してみて。
※先頭に#!/usr/local/bin/perlは必要。下のプログラムは上を書いた「後」に書くこと

$form{'d2'} =~ s/\r\n/\n/g;		#TEXTAREで二重改行防止
print "Content-type: text/html\n\n";
print <<"EOF";
   <html><head>
   <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>アップロードサンプルプログラム</title></head>

   <body>
   <form method="post" action="" ENCTYPE="multipart/form-data">
   d1<input type="text" name="d1" value=$form{'d1'} ><br>
   d2<textarea name="d2">$form{'d2'}</textarea>←↑適当に文字を入力<br>
   FILE1<input type="file" name="FILE1"><br>
   ↑とりあえず画像をアップしてみて下さい。<br>
   ※このサンプルではファイル名にスペースなど特殊があるとエラーが出ます。<br>
   <input type="submit" value="送信">
   </form>
   <hr>

   結果(実行後表示されます)<br>
   d1=「$form{'d1'}」<br>
   d2=「$form{'d1'}」<br>
   FILE1=「$form{'FILE1'}」(ファイル名)<br>
   <img src=$form{'FILE1'}>(画像)<br>
   </body>
   </html>
EOF

if ($form{'FILE1'}){
   open(OUT, ">$form{'FILE1'}");
   binmode(OUT);
   print OUT $formfile{'FILE1'};
   close(OUT);
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA