Blog
Flash(AS3)でcrossdomain.xml無しにクロスドメインにアクセスする
たまにはFlashの技術メモ。常識なのかもしれないが、昨晩ちょっとひっかかった部分なので、備忘録的に…
Flashの仕様でやっかいなのは、ドメインをまたいで外部の情報を読み込む場合、参照側のサーバーに、カスタムポリシーファイルcrossdomain.xmlがないといけない。独自に参照側も作成する場合はcrossdomain.xmlを作成すれば問題ないのだけれど(詳細はこちら→Flashヘルプ – ドメイン間のデータロード許可)、外部のWebAPIを利用する際にはcrossdomain.xmlを設置していないサービスも多く、ちょっとやっかいな問題。
昨晩は、GoogleMapsのジオコーディングの情報をFlashに読み込む必要があったのだが、GoogleMapsのサーバにはcrossdomain.xmlがないためそのまま素直にはAS3でAPIを叩いても情報を取得できない。ということで解決策を調べてみた。
参考にしたのは、下記のサイト。この例ではYouTubeのvideos of the dayのAPIを例にしている。
ここで採用している方法は、PHPでProxyを作成して、それを経由して情報を取得するという方法。つまり、PHPからだとcrossdomain.xml無しに情報にアクセスできるので、AS3からはローカルに置いたPHPを参照して、これを経由してGoogleなどのサイトにアクセスするということ。
まずは、Proxyとなる、crossdomain-proxy.phpを用意する。
<?php $post_data = $HTTP_RAW_POST_DATA; $header[] = "Content-type: text/xml"; $header[] = "Content-length: ".strlen($post_data); preg_match("/url=(.*)/",$_SERVER['REQUEST_URI'],$params); $ch = curl_init( $params[1] ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); if ( strlen($post_data)>0 ){ curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); } $response = curl_exec($ch); if (curl_errno($ch)) { print curl_error($ch); } else { curl_close($ch); header("Content-type: text/xml; Content-length: ".strlen($response)); print $response; } ?>
これを、最終的にデプロイするswfファイルと同じドメインの任意の場所にアップロードする。このPHPをAS3から呼び出すのはこんな感じ。
var GOOGLE_ID:String = "[GoogleAPIのデベロッパID]"; var _crossdomain_proxy:String = "[アップロードしたPHPファイルのパス]"+"crossdomain-proxy.php?url="; var xmlLoader:URLLoader = new URLLoader(); xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGeocoderrorHandler); xmlLoader.addEventListener(Event.COMPLETE, onGeocodeHandleComplete); xmlLoader.dataFormat = URLLoaderDataFormat.TEXT; xmlLoader.load(new URLRequest(_crossdomain_proxy + "http://maps.google.com/maps/geo?q="+"[サーチするテキスト]"+"&output=csv&key="+GOOGLE_ID));
簡単なサンプルも添付しておきます。例えばこれとModestMapsなどを組み合わせれば、ジオコードをFlash上のマップとリンクできるわけです。ここらへんの詳細はまたの機会に。
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.events.IOErrorEvent; import flash.net.*; import flash.xml.*; import fl.controls.Button; import fl.controls.Label; import fl.controls.TextInput; import flash.text.TextField; import flash.text.TextFormat; public class CrossdomainSample extends Sprite { private static var GOOGLE_ID:String = "[Google APIのデベロッパID]"; private var _crossdomain_proxy:String = "https://yoppa.org/works/crossdomain_sample/crossdomain-proxy.php?url="; private var _ti:TextInput; private var _searchBtn:Button; private var _status:TextField; public function CrossdomainSample(){ _ti = new TextInput(); _ti.move(20,20); _ti.setSize(240,20); addChild(_ti); _searchBtn = new Button(); _searchBtn.move(262,20); _searchBtn.label = "search"; _searchBtn.setSize(80,20); _searchBtn.addEventListener(MouseEvent.CLICK, onButtonClick); addChild(_searchBtn); _status = new TextField(); _status.defaultTextFormat = new TextFormat("Verdana", 12, 0x000000); _status.border = true; _status.x = 20; _status.y = 50; _status.width = 320; _status.height = 60; addChild(_status); } //Geocodeボタンがクリックされた際の処理 private function onButtonClick(event:Event):void { //プロクシーのPHPを介して、Geocodeにアクセス if(_ti.text != ""){ var xmlLoader:URLLoader = new URLLoader(); xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGeocoderrorHandler); xmlLoader.addEventListener(Event.COMPLETE, onGeocodeHandleComplete); xmlLoader.dataFormat = URLLoaderDataFormat.TEXT; xmlLoader.load(new URLRequest(_crossdomain_proxy + "http://maps.google.com/maps/geo?q="+encodeURI(_ti.text)+"&output=csv&key="+GOOGLE_ID)); } } //IOエラー private function onGeocoderrorHandler(event:Event):void { _status.text = "データ読み込みエラー"; } //GeoCode読み込み完了 private function onGeocodeHandleComplete(event:Event):void { trace(event.target.data); var loc:Array = event.target.data.split(","); _status.text = "緯度: "+loc[2]+", 経度:"+loc[3]; if(loc[2] == 0 && loc[3] == 0){ _status.text = "そのような場所は存在しません"; } } } }