初級 jQuery PHP ファイル送信

「追加」ボタンで入力欄を増やす(テキスト&画像)

前のサンプルに「画像を選ぶ欄」を加えます。ファイルを送るためのフォームの書き方と、PHP の $_FILES に届くデータの形を確認します。

このサンプルは2017〜2018年ごろに作成したものです。PHPのバージョンが上がり、そのままでは動かなくなっていたため、2026年に PHP 8.3 で動くように書き直し、解説も新しくしました。

DEMO 実際に動かしてみよう

テキストと画像ファイルを1組にした入力欄です。「追加」で行を増やし、画像を選んで「送信」すると、PHP が受け取ったファイルの情報(名前・種類・サイズ)を確認できます。
※ 確認用のデモなので、画像はサーバーに保存されません。

テキスト 画像

解説コードを読んでみよう

このサンプルでわかること

  • ファイルを送信するフォームに必要な enctype="multipart/form-data"
  • 画像の入力欄も name="img[]" で増やせること
  • PHP の $_FILES に届くデータの形(name・type・size・error)

行を増やす仕組みは前のサンプル(テキスト&テキスト)とまったく同じです。ここでは、2列目を画像ファイルの入力欄に変えたときに何が変わるのかを見ていきます。

使っているファイル

  • index.php入力フォームと「追加」の JavaScript
  • data.php送信されたテキストとファイルの情報を表示する PHP

1. ファイルを送れるフォームにする

index.php(13〜35行目) PHP
<form action="data.php" method="post" enctype="multipart/form-data">
<table class="add-form">
  <thead>
    <tr>
      <th>テキスト</th>
      <th>画像</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><input type="text" name="text[]"></td>
      <td><input type="file" name="img[]" accept="image/*"></td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="2"><button id="add" class="btn btn-default" type="button">+ 追加</button></td>
    </tr>
  </tfoot>
</table>
<hr>
<input type="submit" class="btn btn-default" name="send" value="送信">
</form>

ポイントは2つです。

  1. <form> に enctype="multipart/form-data" を付ける。
  2. ファイルの入力欄 <input type="file"> にも、名前の最後に [] を付けて name="img[]" にする。accept="image/*" は「画像ファイルだけ選べるようにする」という指定です。

enctype="multipart/form-data" を書き忘れると、ファイルの名前だけが文字として送られ、中身は届きません。$_FILES も空になります。ファイルを送るフォームでは必ず付けましょう。

2. 「追加」ボタンの JavaScript

追加する1行分の HTML に、ファイルの入力欄が入っただけです。

index.php(37〜53行目) PHP
<script>
$(function() {

  // 「追加」ボタンで、テキストと画像の入力欄を1行追加する
  $('button#add').click(function(){

    var tr_row = '' +
    '<tr>' +
      '<td><input type="text" name="text[]"></td>' +
      '<td><input type="file" name="img[]" accept="image/*"></td>' +
    '</tr>';

    $(tr_row).appendTo($('.add-form > tbody'));
  });

});
</script>

3. PHP で受け取る:$_FILES の形に注意

テキストは $_POST、ファイルは $_FILES に届きます。name="img[]" で複数送ったときの $_FILES は、少し変わった形をしています。

print_r($_FILES) の結果(2行送信した場合) TEXT
Array
(
    [img] => Array
        (
            [name]     => Array ( [0] => cat.jpg     [1] => dog.png )
            [type]     => Array ( [0] => image/jpeg  [1] => image/png )
            [tmp_name] => Array ( [0] => /tmp/phpA1b2 [1] => /tmp/phpC3d4 )
            [error]    => Array ( [0] => 0           [1] => 0 )
            [size]     => Array ( [0] => 34567       [1] => 12345 )
        )
)

「1つ目のファイルの情報」がひとまとまりになっているのではなく、項目ごと(name、type …)に配列になっています。そのため、1つ目のファイル名は $_FILES['img']['name'][0]、2つ目のサイズは $_FILES['img']['size'][1] のように書きます。

項目中身
name選んだファイルの元の名前
typeファイルの種類(ブラウザが申告した値なので、信用しすぎないこと)
tmp_nameサーバー上に一時保存された場所。処理が終わると自動で消えます
error受け取り結果の番号。0(UPLOAD_ERR_OK)なら成功、4 はファイル未選択
sizeファイルの大きさ(バイト)

data.php では、テキストの番号 $i を使って、同じ番号のファイル情報を取り出しています。

data.php(31〜41行目) PHP
<?php foreach ($text as $i => $value):
        $err = (int)($files['error'][$i] ?? UPLOAD_ERR_NO_FILE); ?>
  <tr>
    <td><?= h($i) ?></td>
    <td><?= h($value) ?></td>
    <td><?= h($files['name'][$i] ?? '') ?></td>
    <td><?= h($files['type'][$i] ?? '') ?></td>
    <td><?= h(number_format((int)($files['size'][$i] ?? 0))) ?> バイト</td>
    <td><?= h($upload_errors[$err] ?? 'エラー番号 ' . $err) ?></td>
  </tr>
<?php endforeach; ?>

error の番号は、そのままでは意味が分かりにくいので、先に日本語の対応表を作っておき、番号から説明文を引けるようにしています。

data.php(9〜16行目) PHP
// アップロードのエラー番号を日本語にする
$upload_errors = [
    UPLOAD_ERR_OK         => '正常に受け取りました',
    UPLOAD_ERR_INI_SIZE   => 'サーバーの上限サイズを超えています',
    UPLOAD_ERR_FORM_SIZE  => 'フォームで指定した上限サイズを超えています',
    UPLOAD_ERR_PARTIAL    => '途中までしか届きませんでした',
    UPLOAD_ERR_NO_FILE    => 'ファイルが選ばれていません',
];

このサンプルはファイルを受け取って情報を表示するだけです。tmp_name の一時ファイルは処理が終わると消えるので、サーバーには残りません。実際に保存する方法は「データベースに保存する」のサンプルで解説しています。

PHP 8.3 で書き直したポイント

送信せずに data.php を開いたときや、画像を選ばなかった行があるときに警告が出ないよう、?? [] や ?? '' で「値がないときの代わりの値」を決めてから使っています。

画像を選ばずに送信したり、画像ではないファイルを選んだりして、error や type がどう変わるか確かめてみましょう。

↑