jiichan.com

PROGRAMMING

XMLHttpRequest Lebel2を試してみる

Ajaxには欠かせないXMLHttpRequestですが、いつの間にかLebel2になっていました。
今まではテキストやJSONなど通信できるデータが限られていましたが、 Lebel2になってバイナリなどいろいろなデータを扱えるようになったようです。
そこで早速Lebel2を試してみたいと思います。
なお、例示している送受信のコードはクリックなどのイベント部分は省略してあります。

テキストデータを受信

リクエストのURLにテキストファイルを指定しテキストデータを受信します。


var xhr = new XMLHttpRequest();
xhr.open('GET', './xhrResText.txt', true);
xhr.responseType = 'text';
xhr.onload = function(e) {
	if (xhr.status == 200) {
		alert(xhr.response);
	}
};
xhr.send();

受信するテキストファイルの中身は下記のとおりです。


このテキストファイルはXMLHttpRequestのテストのためのファイルです。

テスト

ここにテキストファイルの中身が表示されます。

JSONデータを受信

level1との違いはJSON.parseする必要が無くなっただけですが取りあえず。
JSONのサンプルデータは下記のとおりです。


[
  {
   "team":"A",
   "member":[
            {"name":"田中", "age":25, "sex":"男"},
            {"name":"佐藤", "age":42, "sex":"男"},
            {"name":"鈴木", "age":32, "sex":"女"}
           ]
  },
  {
   "team":"B",
   "member":[
            {"name":"山本", "age":40, "sex":"女"},
            {"name":"伊藤", "age":35, "sex":"男"},
            {"name":"山田", "age":28, "sex":"女"}
           ]
  }
]

responseTypeにjsonを指定することでJSON.parseしなくてもいいようです。恐らく。


var xhr = new XMLHttpRequest();
xhr.open('GET', './game.txt', true);
xhr.responseType = 'json';
xhr.onload = function(e) {
	if (xhr.status === 200) {
		var data = xhr.response;
		for(var i in data){
			var team = data[i].team;
			var member = data[i].member;
			document.querySelector("#resJson").innerHTML +=
													 "チーム" + team + "<br>";
			for(var key in member){
				 document.querySelector("#resJson").innerHTML +=
								member[key].name + ":" + 
								member[key].age + "歳:" + 
								member[key].sex + "<br>";
			}
		}
	}
};
xhr.send();

テスト

ここにJSONファイルの中身が表示されます。

バイナリデータを受信

リクエストのURLにバイナリファイルを指定し受信して見ます。
テスト用の受信ファイルは庭のサツキを撮った画像(jpg)です。


var xhr = new XMLHttpRequest();
xhr.open('GET', './xhrResBlob.jpg', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
	if (xhr.status == 200) {
		document.querySelector("#resBlob").src = 
			window.URL.createObjectURL(xhr.response);
	}
};
xhr.send();

3行目の responseType は blob (Binary Large OBject:データベースシステムで用いられるデータ型のひとつ)になっています。
7行目の response の中身は blob ですので、createObjectURL でURLテキストにエンコードし、imgタグのsrc属性に渡すことで画像が表示されます。

テスト

テキストデータを送信

今度はデータの送信です。
send メソッドに送信するテキストを直接記述しました。
POST メソッドで送信なのでサーバー側PHPは連想配列(キー=値のペア)で受け取ることになります。
そのため send する前にヘッダで form データであることを明示します(9行目)。


var xhr = new XMLHttpRequest();
xhr.open('POST', './xhrSendText.php', true);
xhr.responseType = 'text';
xhr.onload = function(e) {
	if (xhr.status == 200) {
		alert(xhr.response);
	}
};
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send('moji=これが送られた文章です');

下記がサーバー側PHPで、受け取ったテキストデータをキーと値に分けたものを返しています。


$key = "";
$val = "";
foreach ($_POST as $k=>$v){
	$key .= $k;
	$val .= $v;
}
echo "キー:".$key."\n";
echo "値:".$val;

テスト

バイナリデータを送信

バイナリ(画像)ファイルを送信してみます。
<input type="file">で選択されたものを送信します。


// inputタグで選択されたファイルを取得
var file = this.files[0];

var xhr = new XMLHttpRequest();
xhr.open('POST', './xhrSendBlob.php', true);
xhr.onload = function () {
  if (this.status === 200) {
		alert(xhr.response);
  }
}
xhr.setRequestHeader("Content-Type" , "image/jpeg");
xhr.send(file);

サーバー側PHPで受け取るファイルをjpegのみにしたい、などの場合以外は11行目のヘッダは必要ないと思います。
この送信方法ではファイル名などがサーバー側に通知できないので使い勝手は悪いかと思います。


$headers = getallheaders();

if ($headers["Content-Type"] == "image/jpeg") {
   $blob = file_get_contents("php://input");
   $fp = fopen("./abc.jpg", "w");
   fwrite($fp, $blob);
   fclose($fp);
   echo "画像を受信しました";
}
// php://input はリクエストの body 部から生のデータを読み込むことができる
// php://input は、enctype=”multipart/form-data”に対しては使用できない 

この例ではサーバー側PHPで受け取るファイルをjpegのみにしています。
ファイル名などの情報を取得できないので任意のファイル名にしており汎用性には欠けると思います。

フォームを送信

フォームの送信です。
ファイルやテキストデータもすべて同時に送れるので、データの送信はこれさえ覚えれば良いと思います。


// inputタグで選択されたファイルを取得
var file = this.files[0];

var formData = new FormData();
formData.append('age', 65);
formData.append('sex', "男性");
formData.append('upfile', file);

var xhr = new XMLHttpRequest();
xhr.open('POST', './xhrSendForm.php', true);
xhr.responseType = 'text';
xhr.onload = function(e) {
	if (xhr.status === 200) {
		alert(xhr.response);
	}
};
xhr.send(formData);

HTML内に配置されたformを利用する場合は、new FormData(DOMエレメント)となります。
また、send() の呼び出しで multipart/form-data が付加されて送信されます。


$age = $_POST["age"];
$sex = $_POST["sex"];

// inputのname=upfileでアップロードされたファイル
$name = $_FILES["upfile"]["name"];
$tmp_name = $_FILES["upfile"]["tmp_name"];
// アップロードされたファイルがあるか確認
if (is_uploaded_file($tmp_name)) {
	// 指定の場所にファイルを移動
	if (move_uploaded_file($tmp_name, "./".$name)) {
		echo "年齢は".$age."歳で性別は".$sex."の写真".$name."をアップロードしました。";
	} else {
		echo "ファイルをアップロードできません。";
	}
}
/*
 * $_FILESの内容
 * $_FILES[]["name"]		ファイル名
 * $_FILES[]["type"]		MIMEタイプ
 * $_FILES[]["tmp_name"]一時ファイルのパス
 * $_FILES[]["size"]		ファイルサイズ
 * $_FILES[]["error"]	エラーコード
 */

進捗状況把握

処理の進捗状況を得たい場合はonprogressを使う。
loadedは受信または送信済バイトをtotalは総バイト数を表しています。

ダウンロード


xhr.onprogress = function (e) {
	document.querySelector("#resultStr").innerHTML = e.loaded +"/"+ e.total;
}

アップロード


xhr.upload.onprogress = function (e) {
	document.querySelector("#resultStr").innerHTML = e.loaded +"/"+ e.total;
};



level1に比べバイトデータを扱えて良くなったと思いました。特にFormDataは便利だと思います。
今まではAjaxといえばjQueryを利用していましたがlevel2はコード量も少ないのでこれからは素でいきたいと思っています。