在Jersey JAX-RS服务器中返回大对象时如何设置Content-Length

时间:2014-07-04 10:57:09

标签: jersey jax-rs jersey-2.0

我有时希望返回一个大的(几MB)二进制对象作为JAX-RS资源方法的响应。我知道对象的大小,我希望在响应上设置Content-Length标头,我不希望使用分块传输编码。

在Jersey 1.x中,我使用自定义的MessageBodyWriter解决了这个问题:

public class Blob {
  public InputStream stream;
  public long length;    
}

@Provider
public class BlobWriter extends MessageBodyWriter<Blob> {

  public boolean isWriteable(Class<?> type, Type genericType,
                               Annotation[] annotations, MediaType mediaType) {
    return Blob.class.isAssignableFrom(type);
  }

  public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations,
                        MediaType mediaType) {
    return t.length;
  }

  public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
                      MediaType mediaType,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream entityStream)
            throws java.io.IOException {
    org.glassfish.jersey.message.internal.ReaderWriter.writeTo(t.stream, entityStream);
  }
}

但是当我升级到Jersey 2.x时,这停止了工作,因为JAX-RS / Jersey 2不再关心MessageBodyWriter.getSize()了。我怎样才能用Jersey 2来实现这个目标?

2 个答案:

答案 0 :(得分:4)

似乎可以使用提供的httpHeaders从MessageBodyWriter.writeTo()设置Content-Length标头:

  public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
                      MediaType mediaType,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream entityStream)
            throws java.io.IOException {
    httpHeaders.addFirst(HttpHeaders.CONTENT_LENGTH, t.length.toString);
    org.glassfish.jersey.message.internal.ReaderWriter.writeTo(t.stream, entityStream);
  }

答案 1 :(得分:3)

来自JAX-RS 2.0 ApiDoc

  

从JAX-RS 2.0开始,该方法已被弃用且价值不足   JAX-RS运行时忽略该方法返回的方法。所有   建议MessageBodyWriter实现从中返回-1   方法。负责计算实际的Content-Length标头   值已委托给JAX-RS运行时。

JAX-RS实施还将决定是否会发送Content-Length标头或Transfer-Encoding: chunked

正如您已经了解Content-Length,您可以在ResourceClass中设置一个临时标题,并在ContainerResponseFilter中设置真实标题:

@Path("/foo")
public class SomeResource {

    @GET
    public Blob test() {
        return Response.ok(...).header("X-Content-Length", blob.length()).build();
    }

}

@Provider
public class HeaderFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        String contentLength = responseContext.getHeaderString("X-Content-Length");
        if (contentLength != null) {
            responseContext.getHeaders().remove("Transfer-Encoding");
            responseContext.getHeaders().remove("X-Content-Length");
            responseContext.getHeaders().putSingle("Content-Length", contentLength);
        }
    }

}