java GZIPOutputStream阻止ChunkedOutput http响应

时间:2017-02-26 08:54:47

标签: java jersey gzipoutputstream

我在Jetty上使用Jersey2编写了一个基本的REST服务器,以测试HTTP Chunked Transfer-Encoding和gzip内容编码。但是我发现实现WriterInceptor以将GZIPOutputStream应用于gzip编码的推荐方法导致服务器阻塞而不是通过gzip的块发送。 我相信是GZIPOutputStream等待它自己的缓冲区填满,所以我尝试在WriterInterceptor中重写write()方法以在每次写入后强制刷新()(因为我的服务器总是一次写入一个块)但是没有不同。有没有办法在写入发生时强制刷新?

App.java

public class App
{

    public static int lineCount=0;

    public static void main( String[] args ) {

        System.out.println( "Hello World!" );

        ResourceConfig config = new ResourceConfig();
        config.packages("com.example.mockAPIjava");
        ServletHolder servlet = new ServletHolder(new ServletContainer(config));

        EncodingFilter.enableFor(config, GZipEncoder.class);

        Server server = new Server(2222);
        ServletContextHandler context = new ServletContextHandler(server, "/*");
        context.addServlet(servlet, "/*");


        try {
            server.start();
            server.join();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            server.destroy();
        }
    }
}

GZIPWriterInterceptor.java

@Provider
@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {

    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
            throws IOException, WebApplicationException {

        MultivaluedMap<String,Object> headers = context.getHeaders();
        headers.add("Content-Encoding", "gzip");

        final OutputStream outputStream = context.getOutputStream();
        context.setOutputStream(new GZIPOutputStream(outputStream) {
            @Override
            public void write(final int b) throws IOException {
                out.write(b);
                out.flush();
            }

            @Override
            public void write(final byte[] b) throws IOException {
                out.write(b);
                out.flush();
            }

            @Override
            public void write(final byte[] b, final int off, final int len) throws IOException {
                out.write(b, off, len);
                out.flush();
            }
        });
        context.proceed();
    }
}

Resource.java

@Path("stream")
public class Resource {
    @GET
    @Path("test")
    @Compress
    @Produces(MediaType.APPLICATION_JSON)
    public ChunkedOutput<String> helloWorld(@Context HttpHeaders header, @Context HttpServletResponse response) {
        final ChunkedOutput<String> output = new ChunkedOutput<String>(String.class, "\r\n");

        new Thread() {
            public void run() {
                BufferedReader br = null;

                try {
                    String chunk;

                    // open file for reading
                    File file = new File("/tmp/stream.txt");
                    FileReader fr = new FileReader(file);
                    br = new BufferedReader(fr);

                    while ((chunk = getNextString(br)) != null) {
                        // write a chunk every second
                        output.write(chunk);

                        try {
                            Thread.sleep(1 * 1000);
                        } catch(InterruptedException ex) {
                            Thread.currentThread().interrupt();
                        }
                    }

                } catch (Exception e) {
                    // IOException thrown when writing the
                    // chunks of response: should be handled
                    e.printStackTrace();
                } finally {
                    try {        
                        output.close();
                        // simplified: IOException thrown from
                        // this close() should be handled here...
                        if (br!=null) { br.close(); }
                    } catch (IOException e1){
                        e1.printStackTrace();
                    }
                }
            }
        }.start();

        // the output will be probably returned even before
        // a first chunk is written by the new thread
        return output;
    }


    private String getNextString(BufferedReader br) throws IOException, ParseException {
        App.lineCount++;
        return br.readLine();;
    }
}

Compress.java

//@Compress annotation is the name binding annotation for the GZIPWriterInterceptor
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}

1 个答案:

答案 0 :(得分:0)

通过覆盖... { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ { loader: 'css-loader', options: { modules: true } }, 'postcss-loader', 'sass-loader' ] }), }, ... 的{​​{1}}方法,您刚刚停止了gzipping!

write

因为您已将其重写为而不是调用GZIPOutputStream(您应该已经完成​​),而是public void write(final int b) throws IOException { out.write(b); out.flush(); } ,您将直接发送到上下文OutputStream,解压缩。

据推测,接收方期望gzip数据而不接收它,这可能会导致各种错误行为。

更改代码以调用super.writeout.write

super.write