iPhoneで撮った写真が逆さになる問題をブラウザ側で解決する

スマホからフォームで画像をアップロードしてphpで保存したら逆さで保存された - とりあえずphpとか

かなり昔に同じ問題に遭遇した時はサーバーサイドで解決した。

が、jsが進化したからか自分がjs触る機会が増えたからかわからないけど、今回ブラウザで解決しようという発想になったのでメモしておく。

iPhoneで撮影下写真は
exif情報がメタデータに設定される。
exif情報には回転率が含まれている。
・ブラウザでexif情報をみて表示する前提なので、画像自体は回転して保存されている。
・が、Mobile Safari以外のブラウザで見るとexif情報を見ないブラウザもあるので、画像の向きがおかしくなる。

で、前に書いたときはアップロードされる際にexif情報を見て、画像を回転させて保存させていたのだけど。
今回はブラウザ側で、アップロードする前に画像を回転させたものをアップロードするというアプローチ。

exif情報を読み込むためにexif-jsというライブラリを使用した。

index.html(呼ぶ側)

<script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js"></script>
<script src="./iosImgRotate.js"></script>

<input type="file" id="file" />
<img id="img" />

<script>
 var iosImgRotate = new iosImgRotate;
 document
   .getElementById('file')
   .addEventListener('change', function(e) {
     var file = event.target.files[0];
     iosImageRotate
     .convert(file)
     .then(function(result) {
       var img = document.getElementById('img');
       img.src = result.data;
     });
 });
</script>

IosImgRotate.js(呼ばれる側)

(function(mod) {
  window.iosImgRotate = mod(window.EXIF)
})(function(EXIF) {
  'use strict';
  var iosImgRotate = function() {
    return {
      convert: function(file) {
        return new Promise(function(resolve, reject) {
          EXIF.getData(file, function() {
            var reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function(targetFile) {
              var canvas = document.createElement('canvas');
              var ctx = canvas.getContext('2d');
              var img = new Image();
              img.src = targetFile.target.result;
              img.onload = function() {
                switch(file.exifdata.Orientation) {
                case 3:
                  canvas.width = img.width;
                  canvas.height = img.height;
                  ctx.rotate(Math.PI);
                  ctx.drawImage(img, -img.width, -img.height);
                  ctx.rotate(-Math.PI);
                  break;
                case 6:
                  canvas.width = img.height;
                  canvas.height = img.width;
                  ctx.rotate(Math.PI * 0.5);
                  ctx.drawImage(img, 0, -img.height);
                  ctx.rotate(-Math.PI * 0.5);
                  break;
                case 8:
                  canvas.width = img.height;
                  canvas.height = img.width;
                  ctx.rotate(-Math.PI * 0.5);
                  ctx.drawImage(img, -img.width, 0);
                  ctx.rotate(Math.PI * 0.5);
                  break;
                default:
                  canvas.width = img.width;
                  canvas.height = img.height;
                  ctx.drawImage(img, 0, 0);
                  break;
                }
                var base64 = canvas.toDataURL('image/jpeg');
                var tmp = base64.split(',');
                var data = atob(tmp[1]);
                var mime = tmp[0].split(':')[1].split(';')[0];
                var buf = new Uint8Array(data.length);
                for (var i = 0; i < data.length; i++) {
                  buf[i] = data.charCodeAt(i);
                }
                var blob = new Blob([buf], { type: mime });
                var blobs = [ blob ];
                var info = { type: 'image/jpeg', lastModified: 0 };
                var objectFile = new File(blobs, 'image.jpg', info);
                resolve({ file: objectFile, data: base64 });
              };
            };
          });
        });
      }
    }
  }
  return iosImgRotate;
});

ここではズラズラ書いたけど、今後はこちらにライブラリとして使いやすいように作り込んで行こうかと思う。
https://github.com/nrikiji/IosImgRotate

以上です。