§HTTP レスポンスのストリーミング
§標準的なレスポンスと Content-length ヘッダ
HTTP 1.1 以降、複数の HTTP リクエストとレスポンスにまたがって単一のコネクションを使いまわすためには、サーバはレスポンスと一緒に適切な Content-Length
HTTP ヘッダを送信する必要があります。
デフォルトでは、例えば次のような単純な結果を送る場合を考えてみましょう。
public static Result index() {
return ok("Hello World");
}
この例では、Content-Length
を指定していません。送信しようとしているコンテンツが明らかなので、Play が自動的にコンテンツサイズを計算して、適切なヘッダを生成することができます。
文字列をバイト列に変換するために使われたエンコーディングにしたがって
Content-Length
をヘッダを計算しなければならないので、このようなテキストベースのコンテンツを返すことは見た目ほど単純なことではないことに 注意 してください。
Content-Length
ヘッダを正しく計算するため、Play はレスポンスのデータ全てを読んで、内容をメモリにロードしなければなりません。
§ファイルを送信する
シンプルなコンテンツであればメモリに全て読み込んで処理しても問題ありませんが、データセットが巨大な場合はどうでしょうか?例えば、大きなファイルを web クライアントへ送り返すような場合です。
Play には、ローカルファイルを送信するというよくあるタスクを簡単にするためのヘルパが用意されています。
public static Result index() {
return ok(new java.io.File("/tmp/fileToServe.pdf"));
}
このヘルパはファイル名から Content-Type
ヘッダを計算してくれます。さらに、 web ブラウザがどのようにこのレスポンスを取り扱うべきかを指定する Content-Disposition
ヘッダも追加してくれます。デフォルトでは、Content-Disposition: attahment; filename=fileToServe.pdf
という指定により、 web ブラウザはこのファイルのダウンロードをするかどうかをユーザに確認します。
§チャンクレスポンス
今のところ、ストリーミングを始める前にコンテンツの長さを計算することができるため、うまくファイルの内容をストリーミングすることができていました。しかし、コンテンツのサイズが事前にわからないような動的に生成されるコンテンツをストリーミングする場合はどうでしょうか?
このような種類のレスポンスを返すためには、 チャンク転送エンコーディング を利用します。
チャンク転送エンコーディング は HTTP 1.1 で定義されているデータ転送メカニズムの一つで、 web サーバがコンテンツをいくつかのチャンクに分けて送信する、というものです。このレスポンスを送信するためには
Content-Length
ヘッダの代わりにTransfer-Encoding
HTTP レスポンスヘッダを使います。Content-Length
ヘッダがないので、サーバはレスポンスをクライアント(通常は web ブラウザ)へ送信し始める前にコンテンツの長さを知る必要はありません。つまり、 web サーバは動的に生成されるコンテンツの最終的な長さを知ることなく、レスポンスを送り始めることができます。各チャンクの長さはチャンクの内容の直前に送信されます。これによって、クライアントはチャンクの受信が終わったことを認識できます。最後に、長さがゼロのチャンクを送信すると、データ転送は完了です。
http://en.wikipedia.org/wiki/Chunked_transfer_encoding
この方式の利点は、データを ライブ に提供できる、つまり利用できるようになったデータを即座にチャンクとして送信できることです。欠点は、 web ブラウザがコンテンツサイズを知らないため、ダウンロードプログレスバーを正しく表示できないということです。
例として、あるサービスが何かのデータを計算して動的な InputStream
を提供しているケースを考えます。チャンクレスポンスを利用して、 Play にコンテンツを直接的にストリーミングさせるには、次のようにします。
public static Result index() {
InputStream is = getDynamicStreamSomewhere();
return ok(is);
}
自分でチャンクレスポンスのビルダをつくることもできます。 Play Java API は(それぞれ String
と byte[]
によって)テキスト形式とバイナリ両方のチャンクストリームをサポートしています。
public static Result index() {
// Prepare a chunked text stream
Chunks<String> chunks = new StringChunks() {
// Called when the stream is ready
public void onReady(Chunks.Out<String> out) {
registerOutChannelSomewhere(out);
}
};
// Serves this stream with 200 OK
return ok(chunks);
}
onReady
メソッドはストリームへの書き込み準備が整ったときに呼び出されます。引数には書き込み先のチャンネルである Chunks.Out
が渡されます。
さらに( アクター
のような)非同期プロセスがこのストリームにデータを書き込むとします。
public static void registerOutChannelSomewhere(Chunks.Out<String> out) {
out.write("kiki");
out.write("foo");
out.write("bar");
out.close();
}
サーバから送信される HTTP レスポンスは次のようになります。
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
4
kiki
3
foo
3
bar
0
3 つのチャンクと、最後にレスポンスを完了するための空のチャンクが送信されています。
次ページ: Cometソケット