初級 jQuery FileReader ファイル送信

選んだ画像を送信前にプレビューする

画像を選んだ瞬間に、その画像を画面に小さく表示します。JavaScript の FileReader を使い、サーバーに送る前のファイルを読み込む方法を学びます。

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

DEMO 実際に動かしてみよう

画像を選ぶと、送信する前にその場で小さく表示(プレビュー)されます。「追加」で行を増やせます(5行まで)。
※ 確認用のデモなので、画像はサーバーに保存されません。

テキスト 画像

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

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

  • FileReader で、選んだ画像をサーバーに送る前に表示する方法
  • あとから追加した入力欄にも、同じ仕組み(イベント)を付ける方法
  • 追加できる行数に上限を付ける方法

フォームの形と PHP 側の受け取り方は前のサンプル(テキスト&画像)と同じです。今回は JavaScript に「プレビュー」と「行数の上限」を加えます。

使っているファイル

  • index.php入力フォーム、プレビューと「追加」の JavaScript
  • data.php送信された情報を表示する PHP(前のサンプルと同じ内容)

1. プレビューを表示する場所を用意する

画像の入力欄のすぐ上に、空の <div> を置きます。行ごとに区別できるよう、id に番号を付けて view_1、入力欄は file_1 としています。

index.php(23〜29行目) PHP
    <tr>
      <td><input type="text" name="text[]"></td>
      <td>
        <div id="view_1" class="preview"></div>
        <input type="file" id="file_1" name="img[]" accept="image/*">
      </td>
    </tr>

14行目の MAX_FILE_SIZE は、PHP に「この大きさ(バイト)を超えるファイルは受け取らない」と伝える隠し項目です。1048576 バイト = 1MB です。

2. FileReader で画像を読み込んで表示する

index.php(46〜60行目) PHP
    // n 番目の画像欄で画像が選ばれたら、プレビューを表示する
    function fileChange(n) {
        $('#file_' + n).on('change', function () {
            var file = this.files[0];
            if (!file) {                      // 選択を取り消したとき
                $('#view_' + n).empty();
                return;
            }
            var reader = new FileReader();    // ファイルを読み込む道具
            reader.onload = function (e) {    // 読み込みが終わったら
                $('#view_' + n).html('<img src="' + e.target.result + '" alt="">');
            };
            reader.readAsDataURL(file);       // 画像を「データURL」として読み込む
        });
    }
  1. .on('change', ...) … 入力欄でファイルが選ばれた(変わった)ときに動きます。
  2. this.files[0] … 選ばれたファイルの1つ目です。
  3. new FileReader() … ブラウザの中でファイルを読み込むための道具を作ります。
  4. reader.readAsDataURL(file) … 画像を「data:image/png;base64,...」という文字列(データURL)として読み込みます。
  5. reader.onload … 読み込みが終わると呼ばれます。結果 e.target.result を <img src="..."> に入れれば、画像が表示されます。

読み込みは時間がかかることがあるので、「読み込め」と命令したあと、終わったら呼ばれる関数(onload)の中で表示します。readAsDataURL() の次の行で表示しようとしても、まだ読み込みは終わっていません。

ここで画像を読み込んでいるのはブラウザの中だけです。サーバーにはまだ何も送られていません。だから、大きな画像でもすぐに表示できます。

3. 追加した行にもプレビューの仕組みを付ける

index.php(62〜80行目) PHP
    fileChange(num);

    $('button#add').click(function () {
        if ($('.add-form > tbody > tr').length >= max) {
            $('#message').text('※ 追加できるのは' + max + '行までです。');
            return;
        }
        num = num + 1;

        var tr_row = '' +
            '<tr>' +
            '<td><input type="text" name="text[]"></td>' +
            '<td><div id="view_' + num + '" class="preview"></div>' +
            '<input type="file" id="file_' + num + '" name="img[]" accept="image/*"></td>' +
            '</tr>';
        $(tr_row).appendTo($('.add-form > tbody'));

        fileChange(num);   // 追加した行の画像欄にも、プレビューの仕組みを付ける
    });

fileChange(1) を実行した時点で存在しているのは1行目だけです。あとから追加した行には仕組みが付いていないので、行を追加するたびに fileChange(num) を呼び出して、新しい入力欄にも change の処理を付けています(79行目)。

65〜68行目は行数の上限です。表の行の数(.length)が max(5)以上なら、メッセージを出して return で処理を終わらせます。

上限を JavaScript だけで決めても、ブラウザの開発ツールなどを使えば簡単に超えられます。本当に制限したいときは、受け取る PHP 側でも count($_POST['text']) > 5 のようにチェックしましょう。

4. PHP 側の受け取り

data.php は前のサンプルとまったく同じで、$_POST['text'] と $_FILES['img'] を同じ番号で組み合わせて表示しています。

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; ?>

書き直したポイント

  • 以前のコードは FileReader を1つだけ作って使い回し、画像を選び直すたびに load の処理が重なって登録されていました。選ぶたびに新しい FileReader を作るように直しています。
  • 行数の上限は「表の行の数」を数える方法に変えて、確実に5行で止まるようにしました。
  • PHP 側は、値が送られていないときに PHP 8 で警告が出ないよう ?? [] で初期値を決めています。

<input type="file"> に multiple を付けると、1つの欄で複数の画像を選べます。そのときは this.files を for 文で回して、全部をプレビューしてみましょう。

↑