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に変換して引き渡している。