Documentation

You are viewing the documentation for the 2.1.5 release in the 2.1.x series of releases. The latest stable release series is 2.4.x.

§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 は(それぞれ Stringbyte[] によって)テキスト形式とバイナリ両方のチャンクストリームをサポートしています。

public static 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 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ソケット