Exif.js Exif に設定されている回転情報に対応したinput要素(画像)のプレビュー

Exif.js  Exif に設定されている回転情報に対応したinput要素(画像)のプレビュー

どうも藤原です。今年のGWは神がかってますね。弊社は5月1日が創立記念日ですので、 4月最終週は (^-^)です。
本日は 画像の Exif 情報を取得する Exif.js をご紹介いたします。

背景

input 要素に画像を添付した時に FileReader を使ってプレビュー表示していたんですが、 スマホで行うと撮影した画像とプレビューしてる画像の回転があってないことありますよね。 原因はExif (wiki)の回転情報です。 PHPで回転を戻して保存したりすることはあったのですが、JSでプレビューしているときも(POST前)正しく表示したいということです。

Exif.js

抜き出すには結構手間な作業必要なのですが、そんな時見つけたのがExif.jsです。 これで簡単に回転を取得できるようになります。

サンプル

htmlは以下で、プレビュー自体の実装方法は input の label に対して backgrount-imageで指定してあげます。


<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
label {
  display: block;
  width: 250px;
  height: 250px;
  background-size: contain;
  background-repeat: no-repeat;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- exif-js.js の読み込み -->
<script src="/common/js/exif-js.js"></script>
</head>
<body>
<form action="" enctype="multipart/form-data">
<input type="file" name="image1" class="file" accept="image/*" id="image1">
<label for="image1"></label>
</form>
<script src="/common/js/exif.js"></script>
<script src="/common/js/preview.js"></script>
</body>
</html>

preview.js (jQuery)


(function (window, $) {
  'use strict';

  function base64ToArrayBuffer(base64) {
    base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
    var binaryString = atob(base64);
    var len = binaryString.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
  }

  $(function () {
    var $input = $(this).find('input');
    var $label = $(this).find('label');
    $input.on('change', function () {
      if (this.files && this.files[0]) {
        var reader = new FileReader()
        reader.onload = function (e) {
          /** 画像の指定自体は以下のみ **/
          $label
            .css({
              "background-image": 'url(' + e.target.result + ')'
            });
          
          //回転対応 ,  回転具合を見てlabelを回転
          var arrayBuffer = base64ToArrayBuffer(this.result);
          var exif = EXIF.readFromBinaryFile(arrayBuffer);
          var rotate = 0;
          if (exif && exif.Orientation) {
            switch (exif.Orientation) {
              case 3:
                rotate = 180;
                break;
              case 6:
                rotate = 90;
                break;
              case 8:
                rotate = -90;
                break;
            }
            $label.css({
              "transform": "rotate(" + rotate + "deg)",
              "-webkit-transform": "rotate(" + rotate + "deg)"
            });
          }
          //回転対応 ,  回転具合を見てlabelを回転
        }
        reader.readAsDataURL(this.files[0]);
      }
    });
  });
})(window, jQuery);

preview.js(ES6)


const input = document.getElementById('image1');
const label = document.querySelector('label');

const base64ToArrayBuffer = (base64) => {
  base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
  const binaryString = atob(base64);
  const len = binaryString.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

let reader = null;
input.addEventListener('change', () => {
  if (input.files && input.files[0]) {
    reader = new FileReader();
    reader.onload = (e) => {
      label.style.backgroundImage = `url(${e.target.result})`;

      //回転対応 ,  回転具合を見てlabelを回転
      const arrayBuffer = base64ToArrayBuffer(reader.result);
      const exif = EXIF.readFromBinaryFile(arrayBuffer);
      let rotate = 0;
      if (exif && exif.Orientation) {
        switch (exif.Orientation) {
          case 3:
            rotate = 180;
            break;
          case 6:
            rotate = 90;
            break;
          case 8:
            rotate = -90;
            break;
        }
      }
      label.style.transform = `rotate(${rotate}deg)`;
      label.style.webkitTransform = `rotate(${rotate}deg)`;
    }
    reader.readAsDataURL(input.files[0]);
  }
});

ポイント

Exif.jsに読みこませる前に base64ToArrayBuffer という関数で画像データを変換してあげるところですね。
また回転については Exif Orientation Tag を参考に回転させています。(インカメラでとっても反転が無かったので switch には反転用の分岐を今回入れませんでした。必要な場合は 反転時に style の scale を使用して反転する必要があります。)

まとめ

いかがでしたでしょうか?意外と簡単だと思いますので、是非お試しくください。 (良しなに回転させて表示するのが、ユーザーにとって必ずしも正ではないので、使用する場合はご注意ください。)

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ