AS で別ドメインの画像を読み込むときの注意点

ActionScript を使って 外部ドメインの画像を読み込むときの注意点を調べてみた。

AS3 で調べたけど、AS2 でもセキュリティの機構自体は同じ(だと思う)。

読み込み方

Loader クラスを使えば外部ドメインの画像をロードできる

var loader:Loader = new Loader();
var req:URLRequest = new URLRequest("http://www.example.com/sample.gif");
loader.load(req);
addChild(loader);

画像形式は PNG、GIF、JPEG のみ。BMP はダメ。アニメーション GIF の場合は1フレーム目しか描画されない。

読み込み完了したことを知るためには、contentLoaderInfo プロパティの complete イベントを監視すればよい。ファイルが存在しなかったり、ファイル形式が不正だった場合は ioError イベントが発生する。ioError を listen していない場合には例外が投げられる。

loader.contentLoaderInfo.addEventListener("complete", completeHandler);
loader.contentLoaderInfo.addEventListener("ioError", ioErrorHandler);

読み込んだ画像は、回転・拡大したり、透明度を変えたり、フィルタをかけたりすることが可能だ。以下の例を見ていただきたい。

この例では、mixi のロゴを img.mixi.jp のドメインから動的に読み込んでいる。ColorMatrixFilter で紫色に変色させ、DisplacementMapFilter でマウスの上を歪ませている。

ソースは こちら (77行)。(歪ませる部分は miscellaneous [ActionScript 3.0] DisplacementMapFilterで虫眼鏡効果 を参考にさせてもらいました)

セキュリティサンドボックス侵害への対応

外部ドメインから画像を読み込んで、いろいろできるのは分かっていただけただろうか。だが、1つできないことがある。BitmapData.draw() を使って BitmapData に転写しようとすると、セキュリティサンドボックス侵害の例外が発生するのだ。

セキュリティサンドボックス侵害 : BitmapData.draw:http://localhost/ImageLoader1.swfhttp://img.mixi.jp/img/basic/common/mixilogo001.gif にアクセスできません。ポリシーファイルが必要ですが、このメディアがロードされたとき、checkPolicyFile フラグが設定されませんでした。

同様に、Loader で読み込んだ音声ファイルを SoundMixer.computeSpectrum() しようとしたときにも例外が発生する。

このように、外部ドメインからロードしたメディアに対して、ピクセルデータやオーディオデータに直接アクセスすることは許可されない。表示・再生はできるのに、そのメディアを構成するデータを見ることはできない、というわけだ。

対処法は以下の2通り。

1. crossdomain.xml が用意されている場合

画像が置いてあるドメインのクロスドメインポリシーファイル(いわゆる crossdomain.xml)で、アクセスが許可されている場合、データとしてアクセスできる。

(追記) AS2 では crossdomain.xml があっても、BitmapData.draw() できないらしい。

例えば、Flickr のようなサービスでは、全てのドメインからのアクセスを認める設定が記述されている

<?xml version="1.0" ?> 
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*" secure="true" /> 
</cross-domain-policy>

一方、mixi にも crossdomain.xml はあるのだけど、現時点では任意のドメインからのアクセスは許可されていない

<?xml version="1.0" ?> 
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*.mixi.jp" secure="true" /> 
</cross-domain-policy>

では、実際にロード時に crossdomain.xml を読み込ませてみよう。といっても2箇所修正するだけだ。

// LoaderContextを準備
var context:LoaderContext = new LoaderContext(true);

var loader:Loader = new Loader();
var req:URLRequest = new URLRequest("http://www.example.com/sample.gif");
loader.load(req, context); // 第2引数に context を渡す
addChild(loader);

LoaderContext の checkPolicyFile を true にするところがポイント。これ以外に、Security.loadPolicyFile() メソッドを使って、事前に読み込んでおく方法もある。loadPolicyFile() を使うと、crossdomain.xml 以外の URL からクロスドメインポリシーファイルを読み取ることができる。

crossdomain.xml が存在しない場合、もしくは許可されなかった場合にも、画像はロードされる。ロードした段階では例外も発生しない。BitmapData.draw() しようとした段階になって初めて例外が出るので注意が必要だ。

では、どうやって許可されたかを調べるかというと、実際に試してみて例外を拾うのが一番簡単。

loader.contentLoaderInfo.addEventListener("complete", function(event:*):void{
    var bmd:BitmapData = new BitmapData(loader.width, loader.height);
    try{
        bmd.draw(loader);
    }
    catch(e:SecurityError){
        trace(e.toString());
    }
});

もう1つの方法として、loader.contentLoaderInfo.childAllowsParent で調べることもできる。

loader.contentLoaderInfo.addEventListener("complete", function(event:*):void{
    if(loader.contentLoaderInfo.childAllowsParent) {
        // あとはお好きに
    }
});

どちらでもお好きな方を。

具体例を1つ。

assets2.twitter.com から動的に twitter のロゴを読み取って getPixel している。ロゴの上にマウスを持っていくと、マウスの下の色が表示される。ソースは こちら (44行)

(追記)twitter.com の crossdomain.xml の制約が強化されて、getPixel 出来なくなりました。

Firebug の Net タブを見れば、crossdomain.xml とロゴを拾ってきていることが確認できるだろう。

Firebugで確認

2. crossdomain.xml が用意されていない場合

諦めろ。もしくは、自鯖に proxy となる CGI を設置するしかない。

おまけ

その他、注意点、気づいたことなど。

  • crossdomain.xml が用意されているサイトでも、BitmapData として扱う必要がなければ普通に load したほうがよい。リクエストするファイルが少なくなる分、処理が早くなる
  • 今回は Loader で別ドメインの画像を読み取る話だったが、別ドメインの SWF を読み取るときにはもう少し複雑になる。ApplicationDomain や System.allowDomain() が関係してくる。
  • Loader でロードするときには、Cookie も一緒に投げられる。例えば、http://mixi.jp/logout.pl を踏ませることで、mixi からログアウトさせることも可能だし、自分のプロフィールページを開かせて足跡帳を作ることも可能。とはいえ、<img src> で踏ませるのと同じことなので、Flash だから危険、という問題でもない。
  • URLLoader で別ドメインのデータを読み取るときには、必ず crossdomain.xml がチェックされる。URLLoader 自体がデータを読み取るものだから、と考えると納得のいく仕様だろう。

関連URL: