今からスタート! PHP

第12回ファイルのアップロード

Webブラウザから画像などのファイルをアップロードできることは読者もご存知でしょう。ブログのように、文章と画像の両方を同時に投稿するのがあたり前になっているWebサイトも数多く存在します。では、アップロードされたデータをWebアプリケーションではどのように受け取っているのでしょうか。今回はそれについて紹介します。

なお、PHPプログラムでアップロードされたデータを処理するためには、実行環境においてアップロードを受け付けられるように設定されていなくてはなりません。オンラインマニュアルなどで事前にその設定を確認しておいてください。

アップロードのためのフォーム

まず、ファイルをアップロードするためのフォームについて確認しておきましょう。リスト1に画像とそのタイトルを投稿するフォームの例を示します。それをWebブラウザに表示させたものが図1です。このフォームにはポイントが3つ隠れています。

リスト1 画像とそのタイトルを投稿するフォームの例
<form method="post" action="<?php echo $_SERVER[ 'PHP_SELF' ]; ?>"
      enctype="multipart/form-data"><div>
  <input type="hidden" name="MAX_FILE_SIZE" value="1000000">
  タイトル<input type="text" name="title" size="50"><br>
  画像<input type="file" name="picture">
  <input type="submit" value="登録">
</div></form>
図1 リスト1の画面表示
図1 リスト1の画面表示

以下にそのポイントを示します。

  1. <form enctype="multipart/form-data" method="..." action="...">
  2. <input type="hidden" name="MAX_FILE_SIZE"
    value="アップロード可能な最大容量(バイト単位)">
  3. <input type="file" name="...">

1.3.は、ファイルをアップロードする時には最低限必要なHTMLの記述です。

その中で1.は、このフォームからファイルがアップロードされることを示すenctype属性です。他のmethodやactionなどの属性ももちろん必要ですが、アップロードを行う場合はenctype="multipart/form-data" を忘れずに記述しましょう。

2.はPHPプログラムによりデータを受信する場合に必要なもので、アップロード可能な最大の容量を設定するための記述です。容量を示す値はvalue属性にバイト単位で記述します。この値はPHPの実行環境における設定を上回ることはできません。また、このHTMLタグは3.より前に記述しなくてはなりません。

サーバ側には記憶容量や通信時間などの制限がどうしても必要な場合が多いと思いますが、しかしWebブラウザ画面上でアップロードしてよいファイルを選別することはできません。そこで、このような方法で制限するようになっているわけです。

そして3.は実際にアップロードされるデータやファイル名をPHPプログラムに送信するフィールドとなります。Webブラウザの画面上ではファイル名しか指定しませんが、後述するように、実際にはさまざまなデータが送信されます。なお、PHPプログラムでこれらの受信するには、name属性も記述しておかなくてはなりません。

PHPプログラムでのデータ受信

前述のフォームを通じてWebブラウザからファイルがアップロードされると、サーバにデータが送信されます。サーバが受信したデータは、一時ファイルとしてサーバ内の特定の領域に書き込まれ、その内容を含む各種のデータが$_FILESに収められます。この変数によって、表1のようなデータを参照することができます。

表1 $_FILES により受信できるデータ(nameattrは前述3.のname属性の値)
$_FILES['nameattr']['name'] ファイル名
$_FILES['nameattr']['size'] ファイル容量(バイト単位)
$_FILES['nameattr']['tmp_name'] サーバ上の一時ファイル名
$_FILES['nameattr']['error'] エラーコード

そして、$_FILES['nameattr']['error']で表されるによるエラーコードには、UPLOAD_ERR_OK(正常に終了)のほか、いくつかの種類があります。詳細はオンラインマニュアルを参照してください。

なお、複数のファイルを同時にアップロードする時は、リスト2に示すようにフィールドのname属性の値の末尾に[]をつけておき、 PHPプログラム内でそれぞれのファイルについて参照するときは、$_FILES['picture']['name'][ 0 ]などとします。

リスト2 複数のファイルをアップロードするフィールドの例
画像1<input type="file" name="picture[]"><br>
画像2<input type="file" name="picture[]"><br>
画像3<input type="file" name="picture[]">

ファイルでの保存

アップロードによって受信したデータをファイルとして保存してみましょう。まずリスト3に、アップロードされたデータをアップロードされたファイルと同じ名称でimgディレクトリに保存する例を示します。

リスト3 imgディレクトリに同じファイル名でデータを保存する例
// $file は データの一時ファイル名, $name は アップロードしたファイルの名称
$file  = $_FILES[ 'picture' ][ 'tmp_name' ];
$name  = $_FILES[ 'picture' ][ 'name' ];
// 正常にアップロードされていれば、imgディレクトリにデータを保存
if ( $_FILES[ 'picture' ][ 'error' ] == UPLOAD_ERR_OK &&
     is_uploaded_file( $file ) )
  move_uploaded_file( $file, 'img/' . $name );

アップロードが完了したら、まずエラーコードがUPLOAD_ERR_OKであるかを確認します。そしてis_uploaded_file関数によって、受信したデータがアップロードされたものであるかも確認します。この関数の戻り値は、ファイルがHTTPのPOSTメソッドによりアップロードされたものであればTRUEとなります。

データの確認が済んだら、move_uploaded_file関数で一時ファイルから保存用ディレクトリ(ここではimg)にファイルを移動し、データを保存します。ファイルで保存する場合は、保存用ディレクトリおよび保存したファイルのアクセス権限を適切に設定し、アクセス可能なアプリケーションが限定されるようにすべきでしょう。

データベースへの保存

アップロードにより受信したデータは、ファイルとしてだけではなく、データベースに保存することもあるでしょう。リスト4にPostgreSQLのテーブルに保存する例を示します。リスト5にテーブル定義を示します。ここではデータをラージオブジェクトとして保存するようにしています。

リスト4では、ラージオブジェクトを保存するために、SQLのINSERT文中でlo_importというPostgreSQLの関数を用いています。これはファイルにアクセスしてデータを読み込み、その内容をテーブルに保存する関数です。他のデータベースを用いる場合は、それぞれのラージオブジェクトの保存方法に従ってください。なお、データベースに保存する場合も、意図しないデータの漏洩を防ぐ対策を別途施しておくべきでしょう。

リスト4 PostgreSQLのpictureテーブルにデータを保存(例外処理、文字化け対策は省略)
$title = $_POST[ 'title' ];                    // 画像のタイトル
$type  = $_FILES[ 'picture' ][ 'type' ];       // MIMEタイプ
$file  = $_FILES[ 'picture' ][ 'tmp_name' ];   // 一時ファイル名

if ( $_FILES[ 'picture' ][ 'error' ] == UPLOAD_ERR_OK &&
     is_uploaded_file( $file ) )  {
  // データベース接続
  $db = new PDO( 'pgsql:host=localhost port=5432 dbname=postgres', 'postgres', 'password' );
  // トランザクション開始
  $db->beginTransaction();
  // SQL文実行準備
  $stmt = $db->prepare( 'INSERT INTO picture( title, type, data ) VALUES ( :title, :type, lo_import( :file ) )' );
  // SQL文実行
  $stmt->execute( array( ':title' => $title, ':type' => $type, ':file' => $file ) );
  // 実行完了(コミット)
  $db->commit();
  // データベース切断
  unset( $db );
}
リスト5 データを保存するテーブルの例(PostgreSQL用)
CREATE SEQUENCE picid;
CREATE TABLE picture (
  id    INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL( 'picid' ),
  title VARCHAR(200) NOT NULL UNIQUE,
  type  VARCHAR(30) NOT NULL,
  data  oid NOT NULL
);

これまで基本的なPHPプログラミングのトピックをさまざま紹介してきたこの連載ですが、今回で最後となりました。本稿が読者にとってPHPプログラミングに親しむきっかけになったとすれば、筆者としては望外の喜びです。それでは、いつかまたお会いしましょう。

おすすめ記事

記事・ニュース一覧