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.swf は http://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 とロゴを拾ってきていることが確認できるだろう。
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: