カテゴリー別アーカイブ: Ajax

Ajaxによるファイルアップロード

XMLHttpRequest Level 2が実装されるまでは, ファイルのアップロードは, formタグを利用して, Content-Typeをmultipart/form-dataでPOSTしなければなりませんでした.

XMLHttpRequest Level 2を利用すれば, multipart/form-dataによるPOST, つまり, Ajaxによるファイルアップロードも可能になります.

ポイントは, XMLHttpRequestインスタンスのsendメソッドの引数にFormDataインスタンスを指定することです. FormDataのコンストラクタにはHTMLFormElementのインスタンスを指定します.

FormDataはappendメソッドを利用することで, 動的にPOSTするデータを追加することも可能です. また, POSTするデータにファイルが含まれていれば, 自動的にmultipart/form-dataとして送信してくれます (サンプルコードでは, formタグのenctype属性にmultipart/form-dataを指定していますが…).

参考 : XMLHttpRequest2 に関する新しいヒント

FirefoxでXMLHttpRequest Level 2を利用する場合の注意点

FirefoxでXMLHttpRequest Level2 を利用する場合, openメソッドを実行する前にresponseTypeプロパティを設定してしまうと,

InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

というエラーが出てしまいます.

実は解決方法は簡単で, openメソッド移行にresponseTypeを設定すればOKです.
こんな感じ.

var xhr = new XMLHttpRequest();

xhr.open('GET', 'http://....', true);
xhr.responseType = 'arraybuffer';
xhr.send(null);

実は, 長いことこのエラーの原因がわからず, Firefoxの場合だけoverrideMimeTypeを利用する方法でバイナリデータを取得していました. こんな簡単に解決できるとは…

XMLHttpRequest Level 2によるクロスオリジン通信

Ajaxのクロスオリジン通信にはいくつかの手段があり,

  • JSONP
  • iframeハック
  • window.postMessage

これらはいずれも, 厳密にはAjaxを利用するわけではなく, いわばハック的な手段をとることで, あたかもAjax通信しているように見せかけるわけです. したがって, 実装にも少々手間がかかります.

しかし, XMLHttpRequest Level 2を利用すれば, (正式に) Ajaxとしてクロスオリジン通信が非常に簡単な実装で実現可能です.

実装は, サーバーからのレスポンスヘッダーに,

Access-Control-Allow-Origin: 許可するオリジン

を追加するだけです. オリジンの文字列は, http://localhostのように, プロトコル・ホスト (・ポート番号) を指定したものです (最後にスラッシュをつけないように注意してください (http://localhost/)). もし, すべてのオリジンを許可するのであれば, ワイルドカード, つまり, *を指定します.

ただし, XMLHttpRequest Level 2による通信では, Cookieの情報は自動送信されないので, このままではセッション機能などの実装ができません. そこで, withCredentialsプロパティをtrueにしてCookieを送信する指定を明示的にします.

var xhr = new XMLHttpRequest();

// Cookie情報を送信する
xhr.withCredentials = true;

もし, withCredentialsプロパティをtrueにした場合, サーバーからのレスポンスヘッダーを1つ追加する必要があります.

Access-Control-Allow-Credentials: true

このヘッダーを付加する場合, Access-Control-Allow-Originヘッダーでのワイルドカード指定はできないので注意してください.

まとめとして, PHPでの実装例を記載しておきます.

 header('Access-Control-Allow-Origin: http://localhost');
 header('Access-Control-Allow-Credentials: true');

XMLHttpRequest Level2のイベント

XMLHttpRequest Level2のイベントでは, onreadystatechangeではなく, onload, onprogress, onerror, ontimeoutのイベントを利用する

onloadイベント


 

これは, レスポンスを受信完了したときに発生する. onreadystatechangeイベントで, XMLHttpRequestインスタンスのstateプロパティが4 (DONE) になったときと同じ.

xhr.onload = function(event) {
    //レスポンスを受信完了
    //すなわち, xhr.status === xhr.DONE
};

onprogressイベント


 

これは, リクエストを送信してから受信中の間, 定期的に発生する.

onreadystatechangeイベントで, XMLHttpRequestインスタンスのstateプロパティが3 (LOADING) になったときと同じ.

xhr.onprogress = function(event){
    //レスポンスを受信中
    //すなわち, xhr.status === xhr.LOADING
};

おそらく, このonprogressイベントはかなり使えるはず.

というのは, イベントオブジェクトには, FileReaderのonprogressイベントのように, 進捗状況を示すためのプロパティが格納される (インターフェースも全く同じ). これは, XMLHttpRequest Level2以前ではできなかったことでもあります. また, ファイルアップロード時に利用するXMLHttpRequestインスタンスのuploadプロパティも同様に, onprogressイベントと, イベントオブジェクトが定義されています.

xhr.onprogress = function(event){
    //進捗状況を表示
    if (event.lengthComputable && (event.total > 0) {
        var rate = Math.floor((event.loaded / event.total) * 100);
        console.log(rate + ' %');
    }
};

onerrorイベント


 

このイベントは,  XMLHttpRequest Level2以前でも定義されていました. Level 2でも同様に利用します.

xhr.onerror = function(error){
    //エラー処理
};

ontimeoutイベント

最後に, XMLHttpRequest Level2以前では定義されていなかった, ontimeoutイベントを紹介します,

XMLHttpRequest Level2以前ではタイムアウトの処理は自前で実装する必要がありました.

var timerid = window.setTimeout(function(){
    xhr.abort();
}, 60000);

xhr.onreadystatechange = function(){
    if (xhr.status === xhr.DONE) {
        window.clearTimeout(timerid);
    }
};

これは何気にめんどくさい…

XMLHttpRequest Level2ではとてもシンプルにタイムアウト処理を実装可能です.

xhr.timeout = 60000;
xhr.ontimeout = function(){
    //タイムアウト時の処理
};

XMLHttpRequest Level2 はこれらの新しいイベントインターフェースだけでなく, バイナリデータ (ArrayBufferやBlob) の 送受信, FormDataインスタンスの送信, クロスオリジン通信のサポートなど非常に魅力的な技術です. 機会があれば, このブログでも随時紹介していきます.