2013年4月24日水曜日

JavaScriptで画像のファイルタイプをバイナリレベルで判別するには?

JavaScriptで画像のファイルタイプを取得することは結構簡単にできる。問題は、その取得できるファイルタイプが拡張子に依存していることだ。

つまり、本来「DOC」ファイルであるにも関わらず拡張子を「JPG」と偽装すれば、ブラウザは「JPEG」と返してくる。あまりにも甘すぎるじゃないか。

そこで、JavaScriptでファイルタイプをバイナリレベルで判別することができるかどうかやってみたら、できた。

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <title>Deep Image FileType Check</title>
  </head>

  <body>

    <h1>Deep Image FileType Check</h1>
    <h2>(binary level)</h2>
    <input type="file" id="fileSelect" name="files[]" multiple>

    <script>


      // イメージのファイルタイプをバイナリーレベルで調べる
      function getImageFileType(arrayBuffer) {
        var ba = new Uint8Array(arrayBuffer);
        var headerStr = "";
        var headerHex = "";
        for (var i = 0; i < 10; i++) { // 始めの10個分を読む
          headerHex += ba[i].toString(16); // 16進文字列で読む
          headerStr += String.fromCharCode(ba[i]); // 文字列で読む
        }
        var fileType = "unknown";
        if (headerHex.indexOf("ffd8") != -1) { // JPGはヘッダーに「ffd8」を含む
            fileType = "JPG";
        } else if (headerStr.indexOf("PNG") != -1) { // PNGはヘッダーに「PNG」を含む
            fileType = "PNG";
        } else if (headerStr.indexOf("GIF") != -1) { // GIFはヘッダーに「GIF」を含む
            fileType = "GIF";
        } else if (headerStr.indexOf("BM") != -1) { // BMPはヘッダーに「BM」を含む
            fileType = "BMP";
        }
        console.log("fileType=" + fileType + " headerStr=" + headerStr + " headerHex=" + headerHex);
        return fileType;
      } //END getImageFileType()


      // ファイル選択時
      function onChangeFileSelect(evt) {
        var files = evt.target.files;
        for (var i = 0; i < files.length; i++) {
          var fr = new FileReader();
          fr.onload = function(evt) {
            var fileType = getImageFileType(evt.target.result);
            alert("fileType=" + fileType);
          }
          fr.readAsArrayBuffer(files[i]);
        }
      } //END onChangeFileSelect()


      // イベントリスナー追加
      document.getElementById('fileSelect').addEventListener('change', onChangeFileSelect, false);
      

    </script>

  </body>
</html>

このHTMLをブラウザに読み込んで、ボタンをクリックしてファイルを選択すると、このようにアラートが出てファイルタイプを表示する。


ArrayBufferを使えないとダメなので、動作環境は結構限られてくると思うけれど、もはやそんなヘタレブラウザは知らん。

JavaScriptでもバイナリ操作が結構できるようになったなーと思った。