Jersey / JAX-RS:在响应头中返回Content-Length而不是chunked transfer encoding

我使用Jersey创建RESTful API资源,使用ResponseBuilder生成响应。

RESTful资源的示例代码:

 public class infoResource{ @GET @Path("service/{id}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Response getCompany(@PathParam("id")String id) { //company is just a POJO. Company company = getCompany(id); return Response.status(200).entity(company).build(); } } 

在响应中,它在响应头中返回分块传输编码。 “Jersey world”中的正确方法是让它返回Content-Length标头而不是Transfer-Encoding: chunked响应头中的Transfer-Encoding: chunked标头?

选择Content-LengthTransfer-Encoding就是那些容器选择。 这实际上是缓冲区大小的问题。

一种可能的解决方案是提供一个SevletFilter ,它缓冲所有那些编组的字节并设置Content-Length头值。

见本页 。

 @WebFilter public class BufferFilter implements Filter { @Override public void init(FilterConfig filterConfig) { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); // prepare a new ServletResponseWrapper // which returns the buffer as its getOutputStream(); chain.doFilter(...) // now you know how exactly big is your response. final byte[] responseBytes = buffer.toByteArray(); response.setContentLength(responseBytes.length); response.getOutputStream().write(responseBytes); response.flush(); } @Override public void destroy() { } } 

在扩展ResourceConfig的类中,您可以设置缓冲区大小。 超过此大小的响应将被分块,下面将具有Content-Length。

 public class ApplicationConfig extends ResourceConfig { public ApplicationConfig() { //your initialization property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 2000000); } } 

例如,如果从本地文件系统读取输入流,只需添加:

 response.header( "Content-Length", file.length() ); 

查看完整代码以获得更清晰的解释:

 @Path("/files") public class FileDownloadService { private static final String TXT_FILE = "C:\\your file"; @GET @Path("/txt") @Produces(MediaType.APPLICATION_OCTET_STREAM) public Response getTextFile() throws IOException { File file = new File(TXT_FILE); FileInputStream inStream = new FileInputStream(file); ResponseBuilder response = Response.ok((Object) inStream); response.header("Content-Disposition", "attachment; filename=\"filename\""); response.header( "Content-Length", file.length() ); return response.build(); } } 

客户端是Apache HttpClient代码。

可以在此处找到有关StackOverflow上非常类似问题的答案

我在这里复制它以确保它没有转换为注释:

用于执行此操作的一个很好的示例filter,可以从项目中单独使用,是来自github上的Carrot2项目的ContentLengthFilter.java 。

请注意,使用带有字节流的响应包装器来解决问题,因此这也可以确保Transfer-Encoding: Chunked不会被filter链中的某些其他filter/代码设置,并覆盖您的Content-Length标头。组。 您可以通过使用较大的文件对其进行测试来validation,因为它们通常会在响应中进行分块。

我也要在这里复制文件的内容,以确保它不会成为一个断开的链接:

 /* * Carrot2 project. * * Copyright (C) 2002-2010, Dawid Weiss, Stanisław Osiński. * All rights reserved. * * Refer to the full license file "carrot2.LICENSE" * in the root folder of the repository checkout or at: * http://www.carrot2.org/carrot2.LICENSE */ package org.carrot2.webapp; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; /** * Buffer the output from filters below and set accurate Content-Length * header. This header is required by flash, among others, to display progress * information. */ public class ContentLengthFilter implements Filter { private final static class BufferingOutputStream extends ServletOutputStream { private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); @Override public void write(int b) throws IOException { baos.write(b); } @Override public void write(byte [] b) throws IOException { baos.write(b); } @Override public void write(byte [] b, int off, int len) throws IOException { baos.write(b, off, len); } } private final static class BufferingHttpServletResponse extends HttpServletResponseWrapper { private enum StreamType { OUTPUT_STREAM, WRITER } private final HttpServletResponse httpResponse; private StreamType acquired; private PrintWriter writer; private ServletOutputStream outputStream; private boolean buffering; public BufferingHttpServletResponse(HttpServletResponse response) { super(response); httpResponse = response; } @Override public ServletOutputStream getOutputStream() throws IOException { if (acquired == StreamType.WRITER) throw new IllegalStateException("Character stream already acquired."); if (outputStream != null) return outputStream; if (hasContentLength()) { outputStream = super.getOutputStream(); } else { outputStream = new BufferingOutputStream(); buffering = true; } acquired = StreamType.OUTPUT_STREAM; return outputStream; } @Override public PrintWriter getWriter() throws IOException { if (acquired == StreamType.OUTPUT_STREAM) throw new IllegalStateException("Binary stream already acquired."); if (writer != null) return writer; if (hasContentLength()) { writer = super.getWriter(); } else { writer = new PrintWriter(new OutputStreamWriter( getOutputStream(), getCharacterEncoding()), false); } acquired = StreamType.WRITER; return writer; } /** * Returns true if the user set Content-Length * explicitly. */ private boolean hasContentLength() { return super.containsHeader("Content-Length"); } /** * Push out the buffered data. */ public void pushBuffer() throws IOException { if (!buffering) throw new IllegalStateException("Not buffering."); BufferingOutputStream bufferedStream = (BufferingOutputStream) outputStream; byte [] buffer = bufferedStream.baos.toByteArray(); httpResponse.setContentLength(buffer.length); httpResponse.getOutputStream().write(buffer); } } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { final HttpServletResponse response = (HttpServletResponse) resp; final BufferingHttpServletResponse wrapped = new BufferingHttpServletResponse(response); chain.doFilter(req, wrapped); if (wrapped.buffering) { wrapped.pushBuffer(); } } public void destroy() { // Empty } public void init(FilterConfig config) throws ServletException { // Empty } }