質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.96%

HTTPヘッダのTransfer-Encodingはどこで設定されますか(Webサーバ?、APサーバ?、Servletなど作成するアプリケーションで設定?)

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 9,909

jurin

score 15

知りたいこと

JavaEE(Servlet or JAX-RS)で数GBレベルのファイルを
ダウンロードする機能を作っています。大容量データのため、
Transfer-Encodingヘッダを設定する必要があると思っています。

WebサーバをIIS or Apache、APサーバをJavaEE準拠の
アプリケーションサーバで作成しようと思っています。
この構成の場合、Transfer-Encodingヘッダは、
WebサーバそれともAPサーバ、もしくは自作するアプリケーション
(Servlet or JAX-RS)どこで設定するものなのでしょうか。

ちなみに、Embedded GlassFish All In One 3.1.1で
プロトタイプ作ってみました。
動作確認したところHTTPレスポンスヘッダは下記の通り設定され
Transfer-Encodingヘッダが設定されています。
自作するアプリケーション(Servlet or JAX-RS)では
設定していないので、GlassFishのどこかで設定されているようです。

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Oracle Corporation/1.8)
Server: GlassFish Server Open Source Edition 3.1.1
Content-Disposition: attachment; filename="test.zip"
Transfer-Encoding: chunked
Date: Sat, 23 Jan 2016 06:40:32 GMT

ソースコード:自作したアプリケーション(Servlet or JAX-RS)

Servlet

@WebServlet("/servlet/download")
public class Download extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Download() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        final File file = new File("xxx.zip");
        OutputStream output = response.getOutputStream();

        InputStream input= new FileInputStream(file);
        response.setHeader("Content-Disposition", "attachment; filename=\"test.zip\"");
        writeOutput(output,input);

        input.close();
    }

    private void writeOutput(OutputStream output,InputStream is) throws IOException{
        byte[] buf = new byte[1000];
        int i = 0;
        for (int nChunk = is.read(buf); nChunk!=-1; nChunk = is.read(buf))
        {
            System.out.println("writeOutput[" + ++i + "]");
            output.write(buf, 0, nChunk);
        }
    }
}

JAX-RS

@Path("/download")
public class Download {
    @GET
    @Produces("application/zip")
    public Response get(){
        final File file = new File("xxx.zip");

        StreamingOutput stream = new StreamingOutput(){
            @Override
            public void write(java.io.OutputStream os) throws IOException, WebApplicationException {
                InputStream is= new FileInputStream(file);
                byte[] buf = new byte[1000];
                for (int nChunk = is.read(buf); nChunk!=-1; nChunk = is.read(buf))
                {
                    os.write(buf, 0, nChunk);
                }
                is.close();
            }
        };

        return Response
                .status(Response.Status.OK)
                .header("Content-Length",file.length())
                .header("Content-Disposition", "attachment; filename=\"test.zip\"")
                .entity(stream)
                .build(); 
    }
}

補足情報(言語/FW/ツール等のバージョンなど)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

少し概念的なところから説明します。
Transfer-Encodingヘッダは、HTTP1.0には存在せずHTTP1.1で初めてお目見えしました。
HTTP1.0では、Content-lengthというヘッダでレスポンスデータの全ボディサイズを入れて、レスポンスヘッダにくっつけていました。
Content-lengthにはレスポンスデータのボディデータ全ての合計サイズを入れなければいけなかった為、
レスポンスデータを返却するアプリケーションサーバー側で全て処理を行った後じゃないと応答出来ませんでした(処理後じゃないと全サイズが分からない為)。

しかし、時代が進むにつれて、レスポンスデータサイズが大きくなってきた事から全処理後にレスポンスデータを返す事は、リクエスト受信から応答までの時間が伸びてしまい、現実的じゃなくなってきました。
そこでHTTP1.1規定されたのが、Transfer-Encordingヘッダになります。

Transfer-Encordingヘッダの利点は、"レスポンスデータ全体のパケットサイズは知らんけど、とりあえず今送れるパケットサイズはこれだけです"というサイズ値を付与して、送れるデータサイズ分ずつチャンクという単位で分割して順次送信出来る点です。
この事により、クライアント的には、リクエストを投げた後からすぐに順次レスポンスデータを受け取る事が出来、結果全データの応答時間の短縮を実現しています。

**Content-length:レスポンスデータ全サイズ値が入ったヘッダ。
Transfer-Encodingヘッダ:順次データをチャンクという単位で分割して送ってくる時に使用するヘッダ。
**

上記の2つは、どちらか一方を使って、レスポンスデータのボディサイズをクライアント側へ返却する事がHTTPプロトコル上決められています。
Content-lengthヘッダが付く場合は、Transfer-Encordingヘッダは付きませんし、そのまた逆もしかりです。片一方だけ付与されます。
現在だと小さいボディサイズを返す場合はContent-lengthに入れて返却、大きなボディサイズを返す場合は、Transfer-Encordingで分割して返却っていうのが私の経験上、見てきた感じだと多いです。

長くなりましたが、ここまでが前置きです。

[クライアント]⇔[WEBサーバー]⇔[APサーバー]

という構成の場合、どこでContent-lengthで返すか、Transfer-Encordingで分割して返すかを判断しているかというとAPサーバーになります。
何故なら、レスポンスデータのサイズを知っているのはAPサーバーですから。
Weblogicや、JBossなどのAPサーバーはデプロイされたアプリケーションから渡されたレスポンスデータのボディサイズを見て一定を超えたらTransfer-Encordingヘッダ付けてチャンク分割してレスポンスを返してたはずなので。

なので、作成されようとしているアプリケーション内では特に意識される必要は無いと思いますよ。
(APサーバーが勝手に判断して必要に応じて付けてくれるはずです)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/13 14:29

    ご回答ありがとうございます。
    おかげさまで解決しました。

    質問した後、GlassFish以外の商用のAPサーバでも試した結果、
    ご回答いただいた通りAPサーバで判断してTransfer-Encordingを
    つけてることが実感できました。

    あと、概念的な部分の説明もしていただきありがとうございます。
    大変勉強になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.96%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • Javaに関する質問
  • HTTPヘッダのTransfer-Encodingはどこで設定されますか(Webサーバ?、APサーバ?、Servletなど作成するアプリケーションで設定?)