jersey – StreamingOutput作为响应实体

我在Jersey Resource类中实现了流输出。

@GET @Path("xxxxx") @Produces(BulkConstants.TEXT_XML_MEDIA_TYPE}) public Response getFile() { FeedReturnStreamingOutput sout = new FeedReturnStreamingOutput(); response = Response.ok(sout).build(); return response; } class FeedReturnStreamingOutput implements StreamingOutput { public FeedReturnStreamingOutput() @Override public void write(OutputStream outputStream) { //write into Output Stream } } 

问题是尽管在调用FeedReturnStreamingOutput之前从资源发回响应但是Jersey客户端等待直到FeedReturnStreamingOutput执行完成。

客户代码:

 Client client = Client.create(); ClientResponse response = webResource //headers .get(ClientResponse.class); //The codes underneath executes after FeedReturnStreamingOutput is executed which undermines the necessity of streaming OutputStream os = new FileOutputStream("c:\\test\\feedoutput5.txt"); System.out.println(new Date() + " : Reached point A"); if (response.getStatus() == 200) { System.out.println(new Date() + " : Reached point B"); InputStream io = response.getEntityInputStream(); byte[] buff = new byte[1024000]; int count = 0; while ((count = io.read(buff, 0, buff.length)) != -1) { os.write(buff, 0, count); } os.close(); io.close(); } else { System.out.println("Response code :" + response.getStatus()); } System.out.println("Time taken -->> "+(System.currentTimeMillis()-startTime)+" ms"); 

问题是Jersey缓冲实体以缓冲实体以确定Content-Length标头的缓冲OutputStream 。 缓冲区的大小默认为8 kb。 如果需要,可以使用属性禁用缓冲,或者只更改缓冲区的大小

ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER

一个整数值,定义用于缓冲服务器端响应实体的缓冲区大小,以确定其大小并设置HTTP“Content-Length”标头的值。

如果实体大小超过配置的缓冲区大小,则将取消缓冲并且不确定实体大小。 值小于或等于零会禁用实体的缓冲。

可以在服务器端使用此属性来覆盖出站消息缓冲区大小值 – 默认值或使用“jersey.config.contentLength.buffer”全局属性设置的全局自定义值。

默认值为8192。

这是一个例子

 @Path("streaming") public class StreamingResource { @GET @Produces("application/octet-stream") public Response getStream() { return Response.ok(new FeedReturnStreamingOutput()).build(); } public static class FeedReturnStreamingOutput implements StreamingOutput { @Override public void write(OutputStream output) throws IOException, WebApplicationException { try { for (int i = 0; i < 10; i++) { output.write(String.format("Hello %d\n", i).getBytes()); output.flush(); TimeUnit.MILLISECONDS.sleep(500); } } catch (InterruptedException e) { throw new RuntimeException(e); } } } } 

这是没有设置属性的结果

在此处输入图像描述

这是将属性值设置为0后的结果

 public class AppConfig extends ResourceConfig { public AppConfig() { ... property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0); } } 

在此处输入图像描述

尝试从FeedReturnStreamingOutput.write(...)方法调用outputStream.flush() ,每隔X个字节写入输出流或类似的东西。

我猜连接的缓冲区没有填充您要返回的数据。 因此,在Jersey调用outputStream.close()之前,服务不会返回任何内容。

在我的例子中,我有一个流式传输数据的服务,我正在按照你的方式完成:返回Response.ok().build();

我的服务从数据库返回数据,并在将每一行写入输出流后调用outputStream.flush()

我知道服务流数据,因为我可以看到客户端在服务完成发送整个结果之前就开始接收数据。

您的响应太小并且永远不会被分块,因此服务器立即刷新整个请求。 或者你有一个服务器端问题,你的jax-rs库正在等待刷新之前有完整的流。

然而,这看起来更像是客户端问题。 而你似乎正在使用旧版的jersey客户端。

另外, .get(ClientResponse.class)看起来很可疑。

尝试使用现在的JAX-RS标准( 至少在客户端 ):

 import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; Client client = ClientBuilder.newBuilder().build(); WebTarget target = client.target("http://localhost:8080/"); Response response = target.path("path/to/resource").request().get(); 

虽然在类路径中有jersey客户端2.17:

  org.glassfish.jersey.core jersey-client 2.17  

我刚刚意识到如果你使用StreamingOutput的子类,Jersey不会传输响应:

 // works fine Response.ok(new StreamingOutput() { ... }).build(); // don't work public interface MyStreamingOutput extends StreamingOutput { } Response.ok(new MyStreamingOutput() { ... }).build(); 

那是泽西岛的虫子吗?