【改訂版】画像をクライアント側で縮小してプレビューした後にアップロードする

2022-02-25

以前に公開したコードを現在の環境で動作するように改定しました。

以前のものと比べて、複数の画像を受け付けられるようにしたり、規定のサイズにクロップ(切り取り)できるようにしたりしました。

デモ

以下のコードは、そのままHTMLのファイルとして保存すれば自分でも動作を確認することができます。

クライアント側のコード

<!DOCTYPE html>
<html lang="ja">

<head>
   <meta charset="utf-8">
   <title>画像をアップロードする前にプレビュー、縮小する</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no">
</head>

<body>
   <h1>Image縮小サンプル</h1>
   <p>
      <input type="file" id="photo" accept="image/*" multiple="multiple" />
      <label>
         <input type="checkbox" id="crop" /> クロップ
      </label>
   </p>
   <script>
      (function () {
         'use strict';

         // 縮小する画像のサイズ
         var maxWidth = 300;
         var maxHeight = 300;

         document.getElementById('photo').addEventListener('change', resize);

         function resize(e) {
            var crop = document.getElementById('crop').checked;
            console.log('crop:', crop);
            var files = e.target.files;
            for (var i = 0; i < files.length; i++) {

               var file = e.target.files[i];
               imgResize(file, maxWidth, maxHeight, crop).then(d => {
                  var displayImg = document.createElement('img');
                  displayImg.setAttribute('src', d);
                  document.body.appendChild(displayImg);
               })
            }
         }

         var imgResize = (file, tWidth, tHeight, crop) => {
            return new Promise((resolve, reject) => {
               if (!file.type.match(/^image\/(png|jpeg|gif|jp2)$/)) {
                  reject('入力された画像フォーマットに対応していません。')
               }
               var reader = new FileReader();
               var img = new Image();
               reader.onload = () => {
                  img.onload = () => {
                     if (crop && (img.naturalWidth < tWidth || img.naturalHeight < tHeight)) {
                        reject('画像が小さすぎます。<br>幅' + tWidth + 'px、高さ' + tHeight + 'px以上の画像を使用してください。');
                     }
                     var orientation = 1;
                     if (img.src.split(',')[0].match('jpeg')) {
                        if (getComputedStyle(document.body).imageOrientation != 'from-image') {
                           orientation = getOrientation(img.src) || 1;
                        }
                     }
                     draw(img, orientation, tWidth, tHeight, crop).then(resolve);
                  }
                  img.src = reader.result;
               }
               reader.readAsDataURL(file);
            });
         }

         var getOrientation = (imgDataURL) => {
            var byteString = atob(imgDataURL.split(',')[1]);
            var orientaion = byteStringToOrientation(byteString);
            return orientaion;

            function byteStringToOrientation(img) {
               var head = 0;
               var orientation;
               while (1) {
                  if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 218) { break; }
                  if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 216) {
                     head += 2;
                  }
                  else {
                     var length = img.charCodeAt(head + 2) * 256 + img.charCodeAt(head + 3);
                     var endPoint = head + length + 2;
                     if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 225) {
                        var segment = img.slice(head, endPoint);
                        var bigEndian = segment.charCodeAt(10) == 77;
                        if (bigEndian) {
                           var count = segment.charCodeAt(18) * 256 + segment.charCodeAt(19);
                        } else {
                           var count = segment.charCodeAt(18) + segment.charCodeAt(19) * 256;
                        }
                        for (var i = 0; i < count; i++) {
                           var field = segment.slice(20 + 12 * i, 32 + 12 * i);
                           if ((bigEndian && field.charCodeAt(1) == 18) || (!bigEndian && field.charCodeAt(0) == 18)) {
                              orientation = bigEndian ? field.charCodeAt(9) : field.charCodeAt(8);
                           }
                        }
                        break;
                     }
                     head = endPoint;
                  }
                  if (head > img.length) { break; }
               }
               return orientation;
            }
         }

         var draw = (img, orientation, tWidth, tHeight, crop) => {
            return new Promise((resolve, reject) => {
               var canvas = document.createElement('canvas');
               var ctx = canvas.getContext('2d');
               var w = img.naturalWidth;
               var h = img.naturalHeight;

               if (orientation > 4) {
                  [w, h] = [h, w]
               }

               canvas.width = w;
               canvas.height = h;

               switch (orientation) {
                  case 2: ctx.transform(-1, 0, 0, 1, w, 0); break;
                  case 3: ctx.transform(-1, 0, 0, -1, w, h); break;
                  case 4: ctx.transform(1, 0, 0, -1, 0, h); break;
                  case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
                  case 6: ctx.transform(0, 1, -1, 0, w, 0); break;
                  case 7: ctx.transform(0, -1, -1, 0, w, h); break;
                  case 8: ctx.transform(0, -1, 1, 0, 0, h); break;
               }

               var drawImg = (w, h) => {
                  var tRatio = tWidth / tHeight;
                  var sRatio = w / h;
                  var wide = sRatio > tRatio; //作成する画像より元画像が横長の場合
                  var sw, sh, sx, sy;
                  if (!crop) {
                     if (wide) {
                        tHeight = tWidth / sRatio;
                     } else {
                        tWidth = tHeight * sRatio;
                     }
                     sw = w;
                     sh = h;
                     sx = 0;
                     sy = 0;
                  } else {
                     sw = wide ? h * tRatio : w;
                     sh = wide ? h : w / tRatio;
                     sx = wide ? (w - sw) / 2 : 0;
                     sy = wide ? 0 : (h - sh) / 2;
                  }
                  canvas.width = tWidth;
                  canvas.height = tHeight;
                  // ctx.drawImage(img, 0, 0);

                  ctx.drawImage(img, sx, sy, sw, sh, 0, 0, tWidth, tHeight)
                  resolve(canvas.toDataURL('image/jpeg', .75))
               }
               ctx.drawImage(img, 0, 0);
               var data = canvas.toDataURL('image/jpeg', 1)
               img = new Image();
               img.onload = () => {
                  drawImg(w, h);
               }
               img.src = data;
            })
         }
      })();
   </script>
</body>

</html>

ぜひ使ってみてもらえたら嬉しいです。質問などがあれば、右下にあるCommentボタンを押してください。

JavaScriptGoogle App EngineGolang