14 June 2008

PHPでリロード防止したファイルアップロード処理

ブラウザのリロードボタンを押すと、何度も処理が繰り返されるのを阻止したい。例えば、ファイルのアップロードやSQLでのデータ更新などの場合など。

セッション変数を用いて、リロードを検知する方法を取り入れたサンプルプログラム

・初期画面(ファイル選択のテキストボックスが表示されている)でセッション変数(ticket)を定義する。
・セッション変数(ticket)が定義されている時は、アップロード処理する。
・アップロード処理完了後、セッション変数(ticket)をクリアする。

<html> <body> <p>ファイル アップロードのテスト</p> <?php session_start(); if(!empty($_FILES['userfile']['name'])) { if($_SESSION['ticket'] == $_POST['ticket']) { $uploaddir = './'; $uploadfile = $uploaddir . basename($_FILES['userfile']['name']); if(file_exists($uploadfile)) { print("<p>既存ファイルに上書きします</p>"); } if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) { echo "<p>ファイルがアップロードされました</p>"; } else { echo "<p>ファイルのアップロードに失敗しました</p>"; } echo '<p>PHPのファイルアップロード変数($_FILES)の列挙</p>'; while(list($sKey, $sValue) = each($_FILES['userfile'])) { print '$_FILES[userfile][' . $sKey . "] = " . $sValue . "<br>\n"; } $_SESSION = array(); // セッション変数を全てクリア session_destroy(); // セッションファイルを削除 echo '<p><a href="upload_test2.php">送信画面に戻る</a></p>'; } else { $_SESSION = array(); // セッション変数を全てクリア session_destroy(); // セッションファイルを削除 echo '<p>ページのリロードは許可されません</p>'; echo '<p><a href="upload_test2.php">送信画面の表示</a></p>'; } } else { // セッション変数を定義する $_SESSION['ticket'] = md5(uniqid().mt_rand()); ?> <form enctype="multipart/form-data" action="./upload_test2.php" method="POST"> <!-- アップロードするファイルのサイズ上限設定 --> <input type="hidden" name="MAX_FILE_SIZE" value="30000" /> <!-- リロード防止用 セッション変数の設定 --> <input type="hidden" name="ticket" value="<?=$_SESSION['ticket']?>" /> このファイルをアップロード: <input name="userfile" type="file" /> <input type="submit" value="ファイルを送信" /> </form> <?php } ?> </body> </html>

なお、ファイルのアップロード処理終了後に、$_SESSION = array(); session_destroy(); を行って、セッション変数を全てクリアしているが、他の処理でセッション変数を使っている場合は、$_SESSION['ticket'] = ""; のクリアのみ変更する。  また、このサンプルでは、日本語ファイル名のファイル転送は、サーバ側で問題を起こす。


上記のサンプルを、日本語化したもの

<html> <body> <p>ファイル アップロードのテスト</p> <?php mb_language('Japanese'); mb_internal_encoding('Shift_JIS'); mb_http_output('Shift_JIS'); session_start(); ~ 中略 ~ if(!empty($_FILES['userfile']['name'])) { if($_SESSION['ticket'] == $_POST['ticket']) { $uploaddir = './'; $arrBasename = explode('/', $_FILES['userfile']['name']); $uploadfile = $uploaddir . $arrBasename[count($arrBasename)-1]; if(file_exists($uploadfile)) { print("<p>既存ファイルに上書きします</p>"); } if (move_uploaded_file($_FILES['userfile']['tmp_name'], mb_convert_encoding($uploadfile, "UTF-8", "auto"))) { echo "<p>ファイルがアップロードされました</p>"; } else { echo "<p>ファイルのアップロードに失敗しました</p>"; } ~ 以下略 ~

basename( ) 関数が日本語に対応していないため、explode( ) 関数を用いて、ファイル名を取り出している。また、このサンプルファイルはSJISで保存されているが、CentOS(RHEL)ではデフォルト言語がUTF-8のため、パス名をUTF-8に変換して引き渡している。