java Jersey/JAX-RS:在响应头中返回 Content-Length 而不是分块传输编码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11659330/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Jersey/JAX-RS : Return Content-Length in response header instead of chunked transfer encoding
提问by richsinn
I'm using Jersey to create RESTful API resources, and ResponseBuilder
to generate the response.
我正在使用 Jersey 创建 RESTful API 资源并ResponseBuilder
生成响应。
Example code for the RESTful resource:
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();
}
}
In the response, it's returning chunked transfer encoding in the response headers. What is the proper way in the "Jersey world" to have it return the Content-Length
header instead of the Transfer-Encoding: chunked
header in the response headers?
在响应中,它在响应标头中返回分块传输编码。在“泽西世界”中让它返回Content-Length
标头而不是Transfer-Encoding: chunked
响应标头中的标头的正确方法是什么?
采纳答案by Jin Kwon
Selecting Content-Length
or Transfer-Encoding
is just those Containers choice. It's really a matter of buffer size.
选择Content-Length
orTransfer-Encoding
只是那些 Containers 的选择。这实际上是缓冲区大小的问题。
One possible solution is providing a SevletFilter
which buffers all those marshalled bytes and sets Content-Length
header value.
一种可能的解决方案是提供SevletFilter
缓冲所有这些编组字节并设置Content-Length
标头值的 a 。
See this page.
请参阅此页面。
@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() {
}
}
回答by MGH
In you class that extends ResourceConfig you can set the buffer size. Responses above this size will be chunked, below will have Content-Length.
在扩展 ResourceConfig 的类中,您可以设置缓冲区大小。超过此大小的响应将被分块,低于此大小的响应将具有 Content-Length。
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
//your initialization
property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 2000000);
}
}
回答by Octavian Ionel
For example, if your inputstream is read from a local file system, just add:
例如,如果您的输入流是从本地文件系统读取的,只需添加:
response.header( "Content-Length", file.length() );
Check the full code for a clearer explanation:
查看完整代码以获得更清晰的解释:
@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();
}
}
The client side is a Apache HttpClient code.
客户端是一个 Apache HttpClient 代码。
回答by Brad Parks
An answer to a very similar question on StackOverflow can be found here
可以在此处找到有关 StackOverflow 的非常相似问题的答案
I've copied it here to make sure it's not converted to a comment:
我已将其复制到此处以确保它不会转换为评论:
A great sample filter for doing this, that can be used standalone from the project, is this ContentLengthFilter.javafrom the Carrot2project on github.
一个很好的示例过滤器,可以从项目中独立使用,是来自github 上Carrot2项目的ContentLengthFilter.java。
Note that uses a response wrapper with a byte stream to solve the issue, so this also ensures that Transfer-Encoding: Chunked
doesn't get set by some other Filter/code in the filter chain, and override your Content-Length
header when it's set. You can verify that by testing this with larger files, as they'd normally be chunked in the response.
请注意,使用带有字节流的响应包装器来解决该问题,因此这也确保Transfer-Encoding: Chunked
不会被过滤器链中的其他过滤器/代码设置,并Content-Length
在设置时覆盖您的标头。您可以通过使用较大的文件进行测试来验证这一点,因为它们通常会在响应中分块。
I'm going to copy the contents of the file here as well, to ensure it doesn't become a broken link:
我也将在这里复制文件的内容,以确保它不会成为断开的链接:
/*
* 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 <code>Content-Length</code>
* 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 <code>true</code> if the user set <code>Content-Length</code>
* 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
}
}