第 38章ファイルアップロードの処理

目次
POST メソッドによるアップロード
エラーメッセージの説明
陥りやすい落とし穴
複数ファイルのアップロード
PUT メソッドのサポート

POST メソッドによるアップロード

この機能により、テキスト、バイナリファイルの両方をアップロードできるように なります。 PHP の認証機構およびファイル操作関数を用いて、アップロードを許可する ユーザーとアップロード後にそのファイルを使用して行う動作を完全に制御する ことが可能です。

PHP は、全ての RFC-1867 対応ブラウザ(Netscape Navigator 3 以上、 Microsoft からのパッチをあてた Microsoft Internet Explorer 3 または パッチ無しのそれ以降の版を含みます)からファイルのアップロードを 受けることができます。

関係する設定に関する注記: php.inifile_uploads, upload_max_filesize, upload_tmp_dir, post_max_size, max_input_time ディレクティブも参照ください。

PHP は Netscape Composer および W3C の Amaya クライアントにより使用される PUT メソッドによるファイルアップロードもサポートしています。詳細は、PUT メソッドのサポート を参照ください。

例 38-1. ファイルアップロード用のフォーム

ファイルアップロード画面は、次のような特別なフォームを作成すること により、作成することができます。

<!-- The data encoding type, enctype, MUST be specified as below -->
<form enctype="multipart/form-data" action="__URL__" method="POST">
    <!-- MAX_FILE_SIZE must precede the file input field -->
    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
    <!-- Name of input element determines name in $_FILES array -->
    Send this file: <input name="userfile" type="file" />
    <input type="submit" value="Send File" />
</form>

上の例の __URL__ は、PHP ファイルを指すよう置換される 必要があります。

hidden フィールドMAX_FILE_SIZE は、 input フィールド file の前に置く必要があります。 この値は、取得可能なファイルの最大サイズを規定します。この値はバイト数で指定します。 これはブラウザへの勧告に過ぎず、PHP はこれも確認します。 ブラウザ側でこの最大値を出し抜くのは簡単なことなので、この機能を 信頼して、これより大きなサイズのファイルがブロックされることを前提にしては いけません。 しかし、PHP 側の最大サイズの設定を欺くことはできません。 しかしそれでも MAX_FILE_SIZE を指定すべきです。なぜなら、 巨大なファイルを転送しようとして、実はそれが大きすぎて 転送できないということを長時間待ったあとで知らされるのを 防げるからです。

注意: アップロード用のフォームが enctype="multipart/form-data" 属性を有しているかを 確認してください。さもないと、ファイルのアップロードは動作しません。

オートグローバル $_FILES は、PHP 4.1.0 以降に存在します ($HTTP_POST_FILES は、これ以前のバージョンに存在します)。 これらの配列には、全てアップロードされたファイルの情報が 含まれています。

$_FILES の内容は次のようになります。ここでは、上の例のスクリプトで使われたように、 アップロードファイルの名前として userfile を 使用することを仮定していることに注意してください。 実際にはどんな名前にすることもできます。

$_FILES['userfile']['name']

クライアントマシンの元のファイル名。

$_FILES['userfile']['type']

ファイルの MIME 型。ただし、ブラウザがこの情報を提供する場合。 例えば、"image/gif" のようになります。 この MIME 型は PHP 側ではチェックされません。そのため、 この値は信用できません。

$_FILES['userfile']['size']

アップロードされたファイルのバイト単位のサイズ。

$_FILES['userfile']['tmp_name']

アップロードされたファイルがサーバー上で保存されているテンポラ リファイルの名前。

$_FILES['userfile']['error']

このファイルアップロードに関する エラーコード ['error']は、PHP 4.2.0 で追加されました。

php.iniupload_tmp_dir ディレクティブで 他の場所を指定しない限り、ファイルはサーバーにおけるデフォルトの テンポラリディレクトリに保存されます。サーバーのデフォルトディレクトリは、 PHP を実行する環境において環境変数 TMPDIR を設定する ことにより変更することができます。しかし、PHP スクリプトの内部から putenv() 関数により設定しても上手くいきません。 この環境変数は、アップロードされたファイルに他の処理を行う際にも 同様に使用することが可能です。

例 38-2. ファイルのアップロードを検証する

詳細については、is_uploaded_file() および move_uploaded_file() の関数のエントリも 参照ください。以下はフォームからファイルをアップロードするプロセスの例です。

<?php
// 4.1.0より前のPHPでは$FILESの代わりに$HTTP_POST_FILESを使用する必要
// があります。

$uploaddir = '/var/www/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

echo
'<pre>';
if (
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo
"File is valid, and was successfully uploaded.\n";
} else {
    echo
"Possible file upload attack!\n";
}

echo
'Here is some more debugging info:';
print_r($_FILES);

print
"</pre>";

?>

アップロードされたファイルを受け取る PHP スクリプトは、アップロード されたファイルを用いて何をするべきかを決めるために必要なロジックを 全て実装する必要があります。例えば、変数 $_FILES['userfile']['size'] を使用して、小さすぎたり 大きすぎたりするファイルを捨てることができます。指定した型以外の ファイルを全て捨てるために変数 $_FILES['userfile']['type'] を用いることができますが、 これはあくまでいくつかのチェックの中のひとつとしてのみ実行するように してください。なぜなら、この値を設定するのはあくまでもクライアントであり、 PHP 側では何もチェックしていないからです。 PHP 4.2.0 以降、 $_FILES['userfile']['error'] を使用することができ、 エラーコード に基づき、ロジックを構成することができます。 何らかの方法により、テンポラリディレクトリからファイルを削除したり 他の場所に移動したりする必要があります。

フォームでアップロードされるファイルが選択されていない場合、PHP は $_FILES['userfile']['size'] として 0 を返し、 $_FILES['userfile']['tmp_name'] には値を 設定しません。

移動または名前の変更が行われていない場合、リクエストの終了時に そのファイルはテンポラリディレクトリから削除されます。

例 38-3. ファイルの配列をアップロードする

PHP はファイルについても HTML フォームで配列を使用すること をサポートしています。

<form action="" method="post" enctype="multipart/form-data">
<p>Pictures:
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="submit" value="Send" />
</p>
</form>
<?php
foreach ($_FILES["pictures"]["error"] as $key => $error) {
    if (
$error == UPLOAD_ERR_OK) {
        
$tmp_name = $_FILES["pictures"]["tmp_name"][$key];
        
$name = $_FILES["pictures"]["name"][$key];
        
move_uploaded_file($tmp_name, "data/$name");
    }
}
?>