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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 05:56:40  来源:igfitidea点击:

Jersey/JAX-RS : Return Content-Length in response header instead of chunked transfer encoding

javarestjaxbjerseyjax-rs

提问by richsinn

I'm using Jersey to create RESTful API resources, and ResponseBuilderto 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-Lengthheader instead of the Transfer-Encoding: chunkedheader in the response headers?

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

采纳答案by Jin Kwon

Selecting Content-Lengthor Transfer-Encodingis just those Containers choice. It's really a matter of buffer size.

选择Content-LengthorTransfer-Encoding只是那些 Containers 的选择。这实际上是缓冲区大小的问题。

One possible solution is providing a SevletFilterwhich buffers all those marshalled bytes and sets Content-Lengthheader 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: Chunkeddoesn't get set by some other Filter/code in the filter chain, and override your Content-Lengthheader 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
    }
}