2

合計データサイズが不明な場合に、チャンク転送エンコーディングを使用して HttpResponses ボディを提供する HTTP サーバーを実装する Netty 4 の方法に頭を悩ませようとしています。

出発点として、単純に HttpStaticFileServerHandler を変更しました ( https://github.com/netty/netty/tree/netty-4.0.0.CR1/example/src/main/java/io/netty/example/httpにあります)。 /file ) ChunkedFile の代わりに ChunkedStream を使用します (どちらも ChunkedByteInputs です)。

FileInputStream を使用することは、元のサンプル ユース ケースでは理想的ではないことを理解していますが、既知のコードを再利用する良い例になると思います。

したがって、ここに io.netty.example.http.file パッケージの HttpStaticFileServerHandler クラスに対する差分があります (vs. 4.0.0.CR1):

diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
index 904579b..0d3592f 100644
--- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
+++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
@@ -27,13 +27,14 @@ import io.netty.handler.codec.http.FullHttpResponse;
 import io.netty.handler.codec.http.HttpHeaders;
 import io.netty.handler.codec.http.HttpResponse;
 import io.netty.handler.codec.http.HttpResponseStatus;
-import io.netty.handler.stream.ChunkedFile;
+import io.netty.handler.stream.ChunkedStream;
 import io.netty.util.CharsetUtil;

 import javax.activation.MimetypesFileTypeMap;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.RandomAccessFile;
+import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.text.SimpleDateFormat;
@@ -159,17 +160,15 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
             }
         }

-        RandomAccessFile raf;
+        InputStream raf; // Use an InputStream instead of a RandomAccessFile
         try {
-            raf = new RandomAccessFile(file, "r");
+            raf = new FileInputStream(file);
         } catch (FileNotFoundException fnfe) {
             sendError(ctx, NOT_FOUND);
             return;
         }
-        long fileLength = raf.length();

         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
-        setContentLength(response, fileLength);
         setContentTypeHeader(response, file);
         setDateAndCacheHeaders(response, file);
         if (isKeepAlive(request)) {
@@ -180,7 +179,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
         ctx.write(response);

         // Write the content.
-        ChannelFuture writeFuture = ctx.write(new ChunkedFile(raf, 0, fileLength, 8192));
+        ChannelFuture writeFuture = ctx.write(new ChunkedStream(raf)); // Use a ChunkedStream instead of a ChunkedFile

         // Decide whether to close the connection or not.
         if (!isKeepAlive(request)) {

そして、ここで完全に変更されたファイル: https://gist.github.com/eskatos/5311587

変更は最小限です。RandomAccessFile の代わりに FileInputStream を使用し、ChunkedFile の代わりに ChunkedStream を使用します。パイプラインはそのままです。

再現するには、変更を Netty の例に適用し、実行して、任意のファイルをダウンロードしてみてください。

この変更の後、応答はチャンク化されませんが、ファイルのダウンロードはチャンク化されないため、ディレクトリ リストは明らかに機能します。クライアントはファイルをダウンロードしますが、ダウンロードを終了せず、接続を保持して永遠に待ちます。ブラウザーからcurl、wgetなどまでいくつか試しました。また、ByteLoggingHandlerをパイプラインに追加しようとしましたが、空の末尾のチャンクが表示されるため、ブラウザーがまだデータを待機している理由がわかりません。

どんな手掛かり?

4

2 に答える 2

1

不明なサイズ (Content-Length が不明で指定されていない) のチャンク転送を終了するには、空のチャンクを最後のチャンクとして送信するだけです。これにより、キープアライブをサポートするために接続を開いたままにすることができます。

        ctx.write(new ChunkedInputAdapter(new ChunkedStream(raf, 8192)));
        ChannelFuture writeFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

完全な例を次に示します: https://github.com/scireum/sirius/blob/develop/web/src/sirius/web/http/Response.java#L649

于 2014-01-07T22:08:37.003 に答える