【PHP】ファイルアップロードフォームの作成

PHPでファイルアップロード機能を実装する

PHPでファイルアップロードフォームを作成します。

本サンプルのプロジェクトは以下よりダウンロード可能です。(Docker環境で実行可)
Download

├── README.md
├── app
│   ├── data_store.php
│   └── html
│       ├── index.php
│       └── resources
├── db
│   ├── Dockerfile
│   ├── settings
│   │   └── my.cnf
│   └── sqls
│       └── data.sql
├── docker-compose.yml
└── web
    └── Dockerfile

環境: PHP 7.4, MySQL 5.7

アップロードフォーム (SAMPLE)

index.php

<?php

define('FILES_DIR', __DIR__ . '/resources/');
require_once __DIR__ . '/../data_store.php';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    if (empty($_FILES['file']['tmp_name']) || empty($_FILES['file']['name'])) {
        $message = 'ファイルが選択されていません';
        $error = 'class="error"';
    } else {
        list($origin_name, $file_type) = explode('.', $_FILES['file']['name']);

        $filename = bin2hex(openssl_random_pseudo_bytes(16)) . '_' . $origin_name . '.' . $file_type;

        $upload_success = move_uploaded_file($_FILES['file']['tmp_name'], FILES_DIR . $filename);

        $store_result = store($_FILES['file']['name'], $filename, $_FILES["file"]["type"], date("Y-m-d H:i:s"));

        if ($upload_success && $store_result) {
            $message = 'アップロードに成功しました。';
        } else {
            $message = 'アップロードに失敗しました。';
            $error = 'class="error"';
        }
    }
}

?>
<html>
<head>
    <title>PHP SAMPLE</title>
</head>
<body>
    <style>
        .error {
            color: #F00;
        }
    </style>
    <main>
        <h1>php file upload sample</h1>
        <?php if (isset($message)) : ?>
        <p <?= $error?>><?= $message ?></p>
        <?php endif; ?>
        <form action="" enctype="multipart/form-data" method="post">
            <label for="file">ファイル: </label>
            <input id="file" type="file" name="file">
            <button type="submit">アップロード実行</button>
        </form>
    </main>
</body>

</html>

HTMLフォーム

ファイルをアップロードするためには、ファイル選択用のinputタグ(type属性をfileとする)を用意し、formタグのenctype属性で値を「multipart/form-data」と設定します。

PHP定義済み変数 $_FILES

フォームを送信すると、リクエストを受けたPHP側では定義済み変数(スーパーグローバル変数)$_FILESでフォームで選択したファイルの情報が取得できます。

$_FILESに対して、ファイルのinputタグで指定したname属性の値をキーにして色々な情報を参照できます。
サンプルの場合は、$_FILES[‘file’] となります。

$_FILES変数の詳細についてはPHPマニュアルに詳しく記載されていますが、今回のサンプルで必要な情報は以下となります。
https://www.php.net/manual/ja/reserved.variables.files.php

キー参照方法
tmp_nameアップロードした際にサーバ側で作成される一時的な名称。実データを参照する際に使用する。$_FILES[name属性値]['tmp_name']
name実際のファイル名$_FILES[name属性値]['name']
typeファイルの拡張子の情報$_FILES[name属性値]['type']

アップロード実行

PHPで標準で用意されている、move_uploaded_file関数 を使用してアップロードを行います。
https://www.php.net/manual/ja/function.move-uploaded-file

関数の引数に$_FILESで参照できる実データと、サーバ内に保存する画像パスを指定して実行します。

また、サンプルではファイルの意図しない上書きや、まとめて削除されないようにするために元のファイル名に対してアップロードごとにランダムな文字列を追加しています。

データベースへの保存 (SAMPLE)

アップロードしたファイルの情報をMySQLなどデータベースへ保存し管理することができます。

サンプルでは元の画像名、保存名、拡張子データ、日時の列をもつテーブルに保存します。

※ ファイルの実データ(バイナリ)をテーブルに保存することは一般的ではありません。

data_store.php

<?php

define('DSN', 'mysql:host=php-sample-db;dbname=sample;charset=utf8');

function store($origin_name, $filename, $extension, $datetime)
{
    $pdo = new PDO(DSN, 'root', 'password');
    
    $sql = "INSERT INTO files(name, file_name, extension, created_at) 
    VALUES (:name, :file_name, :extension, :created_at);";

    $stmt = $pdo->prepare($sql);

    $stmt->bindValue(":name", $origin_name, PDO::PARAM_STR);
    $stmt->bindValue(":file_name", $filename, PDO::PARAM_STR);
    $stmt->bindValue(":extension", $extension, PDO::PARAM_STR);
    $stmt->bindValue(":created_at", $datetime, PDO::PARAM_STR);

    return $stmt->execute();
}

Follow me!