yoppa.org


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 = "そのような場所は存在しません";
}
}
}
}