[PHPのはじめ方] Part27: ファイルアップロードと画像表示

Webアプリケーションでファイルを受け取り、表示する方法を学びましょう!

1. ファイルアップロードの仕組み

Webサイトでユーザーが自分のコンピュータからサーバーへファイルを送信する機能は、多くのアプリケーションで必要とされます(例:プロフィール画像の設定、レポートの提出など)。

ファイルアップロードは、HTMLのフォームとPHPのサーバーサイド処理を組み合わせて実現します。

  • HTMLフォーム: ユーザーがファイルを選択し、送信するためのインターフェースを提供します。
  • PHPスクリプト: 送信されたファイルを受け取り、サーバー上の指定された場所に保存します。

2. HTMLフォームの作成

ファイルをアップロードするには、HTMLの <form> タグでいくつかの重要な属性を設定する必要があります。

  • method="post": ファイルのような大きなデータを送信する場合、通常POSTメソッドを使用します。
  • enctype="multipart/form-data": ファイルを送信するために必須のエンコーディングタイプです。これを指定しないとファイルデータが正しく送信されません。
  • <input type="file" name="...">: ユーザーがファイルを選択するための入力フィールドです。name属性はPHP側でファイルを参照するために重要です。

記述例 (upload_form.html):

<!DOCTYPE html>
<html lang="ja">
<head> <meta charset="UTF-8"> <title>ファイルアップロードフォーム</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"> <!-- Bulma CSS -->
</head>
<body> <section class="section"> <div class="container"> <h1 class="title">ファイルをアップロード</h1> <form action="upload.php" method="post" enctype="multipart/form-data"> <div class="field"> <label class="label">ファイルを選択してください</label> <div class="control"> <div class="file has-name"> <label class="file-label"> <input class="file-input" type="file" name="uploaded_file" required> <span class="file-cta"> <span class="file-icon"> <i class="fas fa-upload"></i> <!-- Font Awesomeなどのアイコンライブラリが必要 --> </span> <span class="file-label"> ファイルを選択… </span> </span> <span class="file-name"> ファイルが選択されていません </span> </label> </div> </div> <p class="help">画像ファイル (JPEG, PNG, GIF) をアップロードできます。</p> </div> <!-- 隠しフィールドで最大ファイルサイズを指定 (例: 2MB) --> <input type="hidden" name="MAX_FILE_SIZE" value="2097152" /> <div class="field"> <div class="control"> <button type="submit" class="button is-link">アップロード</button> </div> </div> </form> </div> </section> <!-- ファイル名表示のための簡単なJavaScript --> <script> const fileInput = document.querySelector('.file-input'); const fileName = document.querySelector('.file-name'); if (fileInput && fileName) { fileInput.addEventListener('change', () => { if (fileInput.files.length > 0) { fileName.textContent = fileInput.files[0].name; } else { fileName.textContent = 'ファイルが選択されていません'; } }); } </script>
</body>
</html>
ヒント: <input type="hidden" name="MAX_FILE_SIZE" value="..."> は、ブラウザ側でのファイルサイズ制限のヒントとして機能しますが、サーバーサイドでの検証も必ず行う必要があります。値の単位はバイトです。

3. PHPでのファイル受信と処理

フォームから送信されたファイルの情報は、PHPのスーパーグローバル変数 $_FILES に格納されます。$_FILES は連想配列で、フォームの <input type="file">name属性値 (例: uploaded_file) をキーとしてアクセスします。

$_FILES['uploaded_file'] には、さらに以下のキーを持つ連想配列が格納されています:

キー説明
nameアップロードされたファイルの元の名前 (例: my_photo.jpg)
typeファイルのMIMEタイプ (例: image/jpeg)。注意: この値は偽装される可能性があるため、完全に信用してはいけません。
sizeファイルのサイズ (バイト単位)
tmp_nameファイルがサーバー上に一時的に保存されている場所のパス。この一時ファイルはスクリプト終了時に自動的に削除されます。
error アップロードに関するエラーコード。正常にアップロードされた場合は UPLOAD_ERR_OK (値は 0) になります。 エラーコードの詳細はこちら (PHPマニュアル)

ファイルを受け取ったら、まずエラーチェックを行い、問題がなければ一時ファイルを安全な場所に移動させる必要があります。これには move_uploaded_file() 関数を使用します。

記述例 (upload.php):

<?php
// アップロードされたファイル情報があるか、エラーがないかを確認
if (isset($_FILES['uploaded_file']) && $_FILES['uploaded_file']['error'] === UPLOAD_ERR_OK) { $upload_dir = 'uploads/'; // ファイルを保存するディレクトリ (事前に作成し、書き込み権限を与えておく) $tmp_path = $_FILES['uploaded_file']['tmp_name']; // 一時ファイルのパス $file_name = basename($_FILES['uploaded_file']['name']); // 元のファイル名 (危険な文字を削除) $save_path = $upload_dir . $file_name; // 保存先のパス // uploadsディレクトリが存在しない場合は作成 if (!file_exists($upload_dir)) { mkdir($upload_dir, 0777, true); // パーミッションは環境に合わせて調整 } // move_uploaded_file() で一時ファイルを指定した場所へ移動 if (move_uploaded_file($tmp_path, $save_path)) { // アップロード成功時の処理 echo "<p style='color: green;'> ファイル (" . htmlspecialchars($file_name, ENT_QUOTES, 'UTF-8') . ") が正常にアップロードされました。</p>"; // 画像表示の例 (後述) echo "<img src='" . htmlspecialchars($save_path, ENT_QUOTES, 'UTF-8') . "' alt='アップロードされた画像' style='max-width: 300px; margin-top: 15px;'>"; } else { // ファイル移動失敗時の処理 echo "<p style='color: red;'> ファイルの移動中にエラーが発生しました。</p>"; }
} else { // ファイルが選択されていない、またはアップロードエラーが発生した場合 $error_message = 'ファイルが選択されていないか、アップロード中にエラーが発生しました。'; if (isset($_FILES['uploaded_file']['error'])) { switch ($_FILES['uploaded_file']['error']) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: $error_message = 'ファイルサイズが大きすぎます。'; break; case UPLOAD_ERR_NO_FILE: $error_message = 'ファイルが選択されていません。'; break; default: $error_message = '不明なアップロードエラーです。エラーコード: ' . $_FILES['uploaded_file']['error']; } } echo "<p style='color: orange;'>{$error_message}</p>";
}
echo '<p><a href="upload_form.html">戻る</a></p>';
?>

セキュリティ上の注意点

  • 保存ディレクトリ: ファイルを保存するディレクトリ (例: `uploads/`) は、Webサーバーから書き込み可能である必要がありますが、同時にセキュリティにも配慮が必要です。Webルートディレクトリの外に保存するか、.htaccess などで直接アクセスを禁止する設定を推奨します。
  • ファイル名: ユーザーが指定したファイル名をそのまま使うのは危険です。ディレクトリトラバーサル攻撃 (../../.. のようなパスを含む) や、意図しないファイルの上書きを防ぐために、basename() でファイル名部分のみを取得したり、ランダムな文字列やタイムスタンプを付加して一意なファイル名にするなどの対策が必要です。
  • ファイルの種類: $_FILES['uploaded_file']['type'] (MIMEタイプ) は偽装可能なため、信用できません。finfo_file() 関数 (Fileinfo拡張機能が必要) や、ファイルの拡張子をチェックするなど、より信頼性の高い方法でファイルの種類を検証してください。実行可能なスクリプトファイル (.php, .sh など) のアップロードは特に危険なので、許可しないようにしましょう。
  • ファイルサイズ: php.iniupload_max_filesizepost_max_size ディレクティブ、HTMLフォームの MAX_FILE_SIZE、そしてPHPスクリプト内 ($_FILES['uploaded_file']['size']) で、ファイルサイズの上限をチェックし、サーバーリソースの枯渇を防ぎましょう。
  • 権限: アップロードされたファイルや保存ディレクトリのパーミッション(権限)を適切に設定し、意図しない実行や変更を防ぎます。

4. アップロードされた画像の表示

アップロードされたファイルが画像の場合、HTMLの <img> タグを使って表示できます。src属性には、画像ファイルへのパスを指定します。

上記の upload.php の例では、アップロード成功後に以下のように画像を表示しています。

// ... (move_uploaded_file成功後)
echo "<img src='" . htmlspecialchars($save_path, ENT_QUOTES, 'UTF-8') . "' alt='アップロードされた画像' style='max-width: 300px; margin-top: 15px;'>";

ここで重要なのは、$save_path がWebブラウザからアクセス可能なパスである必要がある点です。もしWebルート外に保存した場合は、画像を表示するための別のPHPスクリプト(例:image.php?file=...)を用意し、そのスクリプト内でファイルの内容を読み込んで出力する必要があります。

また、htmlspecialchars() を使ってパスをエスケープし、クロスサイトスクリプティング(XSS)のリスクを軽減しています。

まとめ

ファイルアップロード機能は、ユーザーとのインタラクションを豊かにしますが、セキュリティリスクも伴います。

  • HTMLフォームで method="post"enctype="multipart/form-data" を指定する。
  • PHPでは $_FILES でファイル情報を受け取る。
  • エラーチェック ($_FILES['...']['error']) を必ず行う。
  • move_uploaded_file() で一時ファイルを安全な場所に移動する。
  • ファイル名、種類、サイズに関するセキュリティ対策を徹底する。
  • 画像は <img> タグで表示するが、パスの扱いに注意する。

これらの基本を理解し、安全なファイルアップロード機能を実装しましょう!

コメントを残す

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