2017-02-26 83 views
0

我已經在Jetty之上使用Jersey2編寫了一個基本的REST服務器,用於測試HTTP Chunked Transfer-Encoding和gzip Content-Encoding。不過,我發現實現WriterInceptor以將GZIPOutputStream應用於gzip編碼的推薦方法會導致服務器阻塞,而不是通過gzip的塊發送。 我相信這是GZIPOutputStream等待它自己的緩衝區填滿,所以我試着在WriterInterceptor中重寫write()方法,以便在每次寫入後強制flush()(因爲我的服務器一次只寫一個塊),但是沒有區別。發生寫入時,是否有辦法強制刷新?java GZIPOutputStream塊ChunkedOutput http響應

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 {} 

回答

0

通過重寫的GZIPOutputStreamwrite方法,你剛纔停止從gzip壓縮!

public void write(final int b) throws IOException { 
    out.write(b); 
    out.flush(); 
} 

因爲你已經將其覆蓋到調用super.write(你應該做的),而是out.write,你直接發送到上下文的OutputStream,解壓縮。

據推測,接收方期待gzip數據並沒有收到它,這可能會導致各種錯誤的行爲。

更改代碼來調用super.writeflush

public void write(final int b) throws IOException { 
    super.write(b); 
    flush(); 
} 

+0

Ahh..thanks。我更新了代碼,但行爲沒有改變。當我使用curl調用服務器時,我收到正確的響應頭,然後服務器掛起,直到它關閉流,而不是在寫入時立即發送每個塊。 <日期:孫老師,2017年2月26日22時04分54秒GMT <內容類型:應用程序/ JSON <有所不同:接受編碼 <內容編碼:gzip <傳輸編碼:分塊 <服務器:碼頭(9.2.3.v20140905) < – user3188040