中級 JavaScript jQuery canvas FileReader

画像をブラウザで縮小・拡大する

選んだ画像を、サーバーに送らずブラウザの中だけで縮小します。FileReader で画像を読み込み、canvas に描き直して、縮小した画像をダウンロードできるようにします。

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

DEMO 実際に動かしてみよう

画像を選ぶと、縦・横の長い方が指定したサイズになるように、縦横の比率を保ったまま縮小(拡大)します。画像はサーバーに送らず、ブラウザの中だけで処理します。


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

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

  • <input type="file"> で選んだファイルを JavaScript で受け取る方法
  • FileReader でファイルを読み込み、Image として開く方法
  • canvas に大きさを変えて描き直す、画像縮小のしくみ
  • 縮小した画像をダウンロードできるようにする方法(toBlob)

使っているファイル

  • index.phpデモのページ(HTML・CSS・JavaScript がすべてこの中)

画像をサーバーに送る前にブラウザで小さくしておくと、送る時間が短くなり、サーバーの負担も減ります。スマホで撮った大きな写真を投稿するサービスなどでよく使われる方法です。

1. 画面を作る

サイズを入力する欄、ファイルを選ぶボタン、縮小した画像を描く <canvas>、ダウンロード用のボタンを用意します。ダウンロードボタンは最初は hidden で隠しておきます。

index.php(13〜21行目) HTML
<div class="well">
  <label>長い辺のサイズ:<input type="number" id="size" value="500" min="10" max="4000" style="width:7em"> px</label>
  <hr>
  <input type="file" id="file" accept="image/jpeg,image/png">
</div>

<p id="info" class="resize-info"></p>
<canvas id="canvas" width="0" height="0" class="resize-canvas"></canvas>
<p id="download-area" hidden><a id="download" class="btn btn-primary" download="resized.jpg">縮小した画像をダウンロード</a></p>

2. ファイルが選ばれたら処理を始める

change イベントは「選ばれたファイルが変わったとき」に起きます。まず入力されたサイズを数値に変え、次に選ばれたファイル this.files[0] が JPEG か PNG かを file.type で確認します。

index.php(31〜48行目) JavaScript
  const DEFAULT_SIZE = 500;  // サイズ欄が空のときの長い辺の長さ

  // ファイルが選択されたら
  $('#file').on('change', function() {

    // 1. 指定サイズを数値で取り出す
    let size = parseInt($('#size').val(), 10);
    if (isNaN(size) || size <= 0) {
      size = DEFAULT_SIZE;
    }

    // 2. 選ばれたファイルを取り出し、JPEG か PNG か確認する
    const file = this.files[0];
    if (!file || (file.type !== 'image/jpeg' && file.type !== 'image/png')) {
      alert('JPEG か PNG の画像を選んでください。');
      this.value = '';
      return;
    }

parseInt(値, 10) は文字列を整数に変える関数です。入力欄の値はいつも「文字列」なので、計算に使う前に数値に変えておきます。数字でなかったときは NaN(Not a Number)になるので、isNaN() で確かめて既定値の 500 を使います。

3. ファイルを読み込み、画像として開く

FileReader の readAsDataURL() でファイルを読み込むと、onload の中で e.target.result に「data:image/jpeg;base64,…」という形の文字列が入ります。これを Image の src に入れると、画像として開けます。

index.php(50〜54行目) JavaScript
    // 3. ファイルを読み込んで、画像として開く
    const reader = new FileReader();
    reader.onload = function(e) {
      const image = new Image();
      image.onload = function() {
index.php(84〜87行目) JavaScript
      };
      image.src = e.target.result;
    };
    reader.readAsDataURL(file);

読み込みは時間がかかるので、すぐ次の行には進みません。「読み込み終わったら onload の中を実行する」という順番になっていることに注意しましょう(非同期処理といいます)。

4. 縦横比を保ったサイズを計算する

横長の画像なら横を、縦長の画像なら縦を指定サイズにして、もう一方は元の比率から計算します。

index.php(56〜66行目) JavaScript
        // 4. 縦横比を保ったまま、新しい幅と高さを計算する
        let width, height;
        if (image.width > image.height) {   // 横長
          width  = size;
          height = size * image.height / image.width;
        } else {                            // 縦長・正方形
          height = size;
          width  = size * image.width / image.height;
        }
        width  = Math.round(width);
        height = Math.round(height);

たとえば 4000×3000 の横長写真を 500 にすると、横は 500、縦は 500 × 3000 ÷ 4000 = 375 になります。

5. canvas に描き直す

canvas の大きさを新しいサイズに変えてから、drawImage() で元の画像全体を新しいサイズに縮めて描きます。

index.php(68〜76行目) JavaScript
        // 5. canvas の大きさを変えて、画像を描き直す
        const canvas = document.getElementById('canvas');
        canvas.width  = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, width, height);
        ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height);

        $('#info').text('元の大きさ ' + image.width + '×' + image.height + 'px → ' + width + '×' + height + 'px');
drawImage の引数意味
image描く画像
0, 0, image.width, image.height元の画像のどこを使うか(左上から全体)
0, 0, width, heightcanvas のどこに、どの大きさで描くか

6. ダウンロードできるようにする

canvas.toBlob() で、canvas の絵を JPEG ファイルのデータ(Blob)に変えます。URL.createObjectURL() でそのデータを指す URL を作り、リンクの href に入れると、クリックでダウンロードできます。

index.php(78〜83行目) JavaScript
        // 6. canvas の絵を JPEG のデータ(Blob)にして、ダウンロードできるようにする
        canvas.toBlob(function(blob) {
          const url = URL.createObjectURL(blob);
          $('#download').attr('href', url);
          $('#download-area').prop('hidden', false);
        }, 'image/jpeg', 0.9);

書き直したポイント

古いサンプルでは、canvas の画像を Base64 文字列で取り出し、1文字ずつ数値に直して Blob を作っていました。今は canvas.toBlob() 1つで同じことができます。また、数値以外を入力したときに存在しない命令 exit を呼んでエラーになる部分や、使われていなかったアップロードボタンの処理を整理し、縮小した画像を実際にダウンロードできるようにしました。

toBlob の最後の数字 0.9 は JPEG の画質(0〜1)です。0.5 にするとファイルは小さくなりますが、画質が落ちます。数値を変えてダウンロードしたファイルのサイズを比べてみましょう。

↑