アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

JavaScript だけで EXIF を読む

このブログで公開している WP-ImaGeoMap は EXIF 情報の読み込みに PEL という PHP 向けのライブラリを使っている。

このライブラリは高機能だが容量が 3.15 MB もあり、WP-ImaGeoMap はその中のほんの一部の機能しか使っていない。なのでコンパクトな代替がないかと考えていた。そして最近なにげなくググっていたら、以下のページを見つけた。

ここで紹介されている Jacob Seidelin 氏のスクリプトは前に試そうとして使い方がよくわからなかった。しかしこの記事の解説がとてもわかりやすく、私も仕事で JavaScript を触るようになったので今回はすんなりと利用できた。

ただ利用しておしまいでは理解したとはいえないので、学習のためにソースをリファクタリングしつつコメントを付けてみた。

元ソースのライセンスが Mozilla Public License なのでサンプルもそれに準じる。サンプルを展開して中に入っている index.html を表示するとこんな感じになる。

EXIF を読み込んだところ

IE、Firefox、Chrome で正しく動作することを確認したので、今度は他のサイトの画像とか読んだらどうなるのかなと試してみたら読み込まれない。XMLHttpRequest.onerror をハンドリングすると確かにエラーが発生している。しかし原因が分からない。しばらく悩みながら色々なキーワードでググってみたところ、どうやらクロスドメインの問題にひっかかっているようだ。

スクリプトを読み込んでいるページと同じドメインの画像を指定しないと正しく読み込まれずエラーとなる。この問題と解決方法は以下のページが分かりやすかった。しかしどうやら、JavaScript だけでこの問題を解決するのは無理っぽいことが分かった。

WP-ImaGeoMap は WordPress プラグインなので CGI による解決をおこなえそうだ。もし次に更新することがあったら検討してみたい。この問題さえ注意すれば EXIF 読み込み手段としてかなり有用。元ソースの紹介記事のように Google Maps と組み合わせたり Flickr みたいな写真共有サービスを作るときなどに役立ちそうだ。

Comments from WordPress

  • 万里小路 2010-05-04T08:56:02Z

    ほんとにすごい人がいますね。 私は javascript を始めたばかりなので右も左も分かりません。
    ちょっと教えていただきたいのですが、、、、、

    上のサンプルの exif-reader.zip は、Firefox の時は完全に動くのですが、IEで、かつ Canon のデータの時は動きません。IEで、NikonやOlympusの時は動きます。

    あ、もちろんこのサンプルだけでなく、exis.js が動かないのです。

    デモはこちらに用意しました:
    http://210.155.219.234/Lib/Lib-N99.htm

    どうしてでしょうね?
    binaryajax.js の一番下のところで vbscript で読み込むところがおかしいのかな、とは思いますが、さっぱり分かりません。

    もしおわかりになれば解決策をお教えください。

  • akabeko akabeko 2010-05-04T10:47:58Z

    デモを拝見させていただきました。確かに IE かつ Cannon だと上手く動いていませんね。使用されているスクリプトは Jacob Seidelin 氏の原作版のようなので、デモ中の Canon5D2.jpg の EXIF 情報を、この記事で公開しているスクリプトで表示するデモを作成してみました。

    テストページ :
    http://akabeko.sakura.ne.jp/lab/js/exif/

    すると、IE では上手く EXIF が取れるようですが、逆に Firefox で、Model、Software、Artist の文字列の末尾が化けて表示されるようになりました。データ長の取得あたりに問題がありそうな気がします。

  • 万里小路 2010-05-05T05:49:29Z

    (なぜかポストしたものが現れないので再度)

    はい、 引き続きよろしくお願いします

    1.IE
    ちょっと別件なのかもしれませんが、akabekoさんのIEと私のIEの反応が違うようなのです。 私はWin-7-64のIE8です。 

    まず、bananaのデモをローカルで見ますとIEではEXIFが出ません。 Firefoxでは出ます。 Canon のデモは両方出ます。

    そして、IEでWebでは、Canonのデモはおっしゃるようにnullが出ません。 しかしローカルではIEでもnull文字が出ます。

    2.質問
    bananaのデモからCanonのデモへの変更点は何でしょうか? 動作が変わっています。

    3.希望
    もし今後元のexif.jsではなくakabekoさんの変更されたexif.jsに移るとすると、EXIF.pretty と EXIF.getData は残しておいてほしいのです。 多くの絵のあるページですべてを2度読み込むことを避けたいのです。 

    http://210.155.219.234/Lib/Lib-N24.htm

    の例ではEXIFを見る人はそう多くはないでしょうから。

    function show_exif(i) {
      oImg=document.getElementById("pic"+i)
      EXIF.getData(oImg, function(){
        alert(EXIF.pretty(oImg));
        }
      );
    }

    のように使いたいのです。 exif.js の最後の load の行は消します。

  • 万里小路 2010-05-05T08:35:30Z

    Canon の データをダンプして解析してみました。
    Model の部分はTagの中でデータ長が32バイトとなっています。

    データの中身は32バイトのうち後半の部分にx00が12個あります。 この余分のnullの取り扱いがおそらく binaryajax かexif.jsで混乱しているのではないかと思います。

    akabekoさんのデモで、Model名の後ろに余分な文字が出ているのはおそらくこのnullが化けたものでしょう。

    また、私の最初のデモでEXIT.pretty を使った場合、Model名までしか出なかったのもx00があるからだと思います。

    素人考えで、(元の)exif.js の401行目のCase2の

    return oFile.getStringAt(iStringOffset, iNumValues-1);

    return oFile.getStringAt(iStringOffset, iNumValues-1).replace("\X00","");

    としてみましたが動作しませんでした。  いいところまで迫っているような気はするのですが。

  • akabeko akabeko 2010-05-05T09:25:09Z

    質問への返答です。

    Q1. IE
    ちょっと別件なのかもしれませんが、...後略
    A1.
    私の環境は Windows Vista 32bit、IE8 となります。 IE は 32/64bit で実装が異なるので、そのあたりが挙動の差となっているのかもしれません。

    Q2. 質問
    bananaのデモからCanonのデモへの変更点は何でしょうか? 動作が変わっています。
    A2.
    EXIF 取得対象の写真を、私のサイトにあるものから、万里小路さんのデモにある Canon5D2.jpg に変更しました。 banana の方は、Panasonic の LUMIX で撮影されたものに、ブログの以前の記事で作成したツールで GPS 情報を埋め込んだものとなります。

    Q3. 希望
    もし今後元のexif.jsではなくakabekoさんの変更されたexif.jsに移るとすると ...後略
    A3.
    メソッドの実装方法と名前が変更されていますが、EXIF.pretty と EXIF.getData 相当の機能は残しています。Exif オブジェクトの toStringTags が pretty、getTag が getData に対応しています。

  • akabeko akabeko 2010-05-05T09:57:48Z

    補足です。

    原作版も私が変更したものも、EXIF を読み込んだときに Image オブジェクトへ、exifdata というプロパティとして追加しています。このプロパティは本来の Image オブジェクトには存在しないので、追加することにより、EXIF 情報の記録と共に、読み込み済みであることを示すフラグとしても使用できます。

    原作版では EXIF.getData などに指定された Image オブジェクトについて、このプロパティのチェックを実行し、無ければ EXIF 読み込みを開始します。変更版では Exif オブジェクトのコンストラクタで Image オブジェクトへの参照を記録し、load メソッドで原作版と同様のチェックと読み込みをおこないます。

    つまり EXIF 読み込み処理の機会を、原作版ではメソッドごとに、変更版では load メソッドのみで提供していることになります。設計的には、原作版はユーティリティ的、変更版がオブジェクト的だと思います。getData などに毎回、Image オブジェクトとコールバック指定をするのが面倒なので、変更版では参照をコンストラクタ、読み込みを load メソッドに振り分けました。

    EXIF 情報を読み込んだ Exif オブジェクトのインスタンスが消えても、exifdata プロパティを追加した Image オブジェクトが健在なら、それを指定して Exif オブジェクトを生成することで、load メソッドを呼ばなくても getTag などでデータ取得をおこなえると思います。

  • akabeko akabeko 2010-05-05T10:39:18Z

    もう一つ、補足です。

    このブログのコメント欄は承認制になっているため、送信してもすぐには反映されません。承認を解除することも可能なのですが、コメント方式のスパムが大量に書き込まれるため、やむを得ず、この設定で運用しています。

    分かりにくくて申し訳ないのですが、ご理解をおねがいします。

  • 万里小路 2010-05-05T13:05:48Z

    御協力ありがとうございました。

    まだ分からないことだらけですが、出るようになりました。

    Case2のところで、replace("x00","")をやったのですが、replaceは全部の同じ文字を交換するものとばかり思っていました。 一文字しかしないことが分かり、全部交換したらIEでもうまく表示されるようになりました。、

    ほっとしています。 いろいろお世話になりました。

    今から全ページに組み込みます。

  • akabeko akabeko 2010-05-05T13:23:44Z

    JavaScript だけで EXIF を読む 2 という記事を書きました。よろしければ、お読みください。

    万里小路さんのコメントによりスクリプトを改善できました。ありがとうございます。

  • 万里小路 2010-05-07T03:09:04Z

    ありがとうございました。 私も binaryajax.js の80行目の

    aStr[j] = String.fromCharCode(this.getByteAt(i));

    aStr[j] = String.fromCharCode(this.getByteAt(i)).replace("\x00","");

    と変更して対応いたしました。

  • 万里小路 2010-05-25T22:14:36Z

    binaryajax の作者とコミュニケーションを取りました。 作者も上記(#10)修正を認識しました。

  • akabeko akabeko 2010-05-26T12:30:22Z

    すばらしいです。 改めて作者ページを見ると、アドバイスを受けていろいろと改善し続けているようですね。