Documentation

You are viewing the documentation for the 2.6.x development release. The latest stable release series is 2.4.x.

§HTTP レスポンスのストリーミング

§標準的なレスポンスと Content-length ヘッダ

HTTP 1.1 以降、複数の HTTP リクエストとレスポンスにまたがって単一のコネクションを使いまわすためには、サーバはレスポンスと一緒に適切な Content-Length HTTP ヘッダを送信する必要があります。

デフォルトでは、例えば次のような単純な結果を送る場合を考えてみましょう。

public Result index() {
    return ok("Hello World");
}

この例では、Content-Length を指定していません。送信しようとしているコンテンツが明らかなので、Play が自動的にコンテンツサイズを計算して、適切なヘッダを生成することができます。

文字列をバイト列に変換するために使われたエンコーディングにしたがって Content-Length をヘッダを計算しなければならないので、このようなテキストベースのコンテンツを返すことは見た目ほど単純なことではないことに 注意 してください。

Content-Length ヘッダを正しく計算するため、Play はレスポンスのデータ全てを読んで、内容をメモリにロードしなければなりません。

§ファイルを送信する

シンプルなコンテンツであればメモリに全て読み込んで処理しても問題ありませんが、データセットが巨大な場合はどうでしょうか? 例えば、大きなファイルを web クライアントへ送り返すような場合です。

Play には、ローカルファイルを送信するというよくあるタスクを簡単にするためのヘルパが用意されています。

public 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 サーバは動的に生成されるコンテンツの最終的な長さを知ることなく、レスポンスを送り始めることができます。

各チャンクの長さはチャンクの内容の直前に送信されます。これによって、クライアントはチャンクの受信が終わったことを認識できます。最後に、長さがゼロのチャンクを送信すると、データ転送は完了です。
https://en.wikipedia.org/wiki/Chunked_transfer_encoding

この方式の利点は、データを ライブ に提供できる、つまり利用できるようになったデータを即座にチャンクとして送信できることです。欠点は、web ブラウザがコンテンツサイズを知らないため、ダウンロードプログレスバーを正しく表示できないということです。

例として、あるサービスが何かのデータを計算して動的な InputStream を提供しているケースを考えます。チャンクレスポンスを利用して、Play にコンテンツを直接的にストリーミングさせるには、次のようにします。

public Result index() {
    InputStream is = getDynamicStreamSomewhere();
    return ok(is);
}

自分でチャンクレスポンスのビルダをつくることもできます。 Play Java API は(それぞれ Stringbyte[] によって)テキスト形式とバイナリ両方のチャンクストリームをサポートしています。

public Result index() {
    // Prepare a chunked text stream
    Chunks<String> chunks = StringChunks.whenReady(
            JavaStream::registerOutChannelSomewhere
    );

    // 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 つのチャンクと、最後にレスポンスを完了するための空のチャンクが送信されています。

Next: Comet


このドキュメントの翻訳は Play チームによってメンテナンスされているものではありません。 間違いを見つけた場合、このページのソースコードを ここ で確認することができます。 ドキュメントガイドライン を読んで、お気軽にプルリクエストを送ってください。