2012-07-25 16 views
8

RESTful API kaynakları oluşturmak için Jersey kullanıyorum ve yanıtı oluşturmak için ResponseBuilder. Dinlendirici kaynak içinJersey/JAX-RS: İçerik üstbilgisini yığınlanmış aktarım kodlaması yerine yanıt başlığında döndürme

örnek kod: karşılık

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(); 
    } 
} 

, yanıt başlıklarında yığın halinde transfer kodlama dönüyor. "Jersey dünyasında", yanıt başlığında Transfer-Encoding: chunked başlığı yerine Content-Length başlığını döndürmesini sağlayan uygun yol nedir?

cevap

4

Content-Length veya Transfer-Encoding'un seçilmesi, yalnızca Kapsayıcılar seçimidir. Bu gerçekten bir tampon boyutu meselesi.

Olası bir çözüm, tüm bu özelleştirilmiş baytları arabelleğe alan ve Content-Length başlık değerini ayarlayan bir SevletFilter sağlar.

Bkz. this page. Örneğin

@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() { 
    } 
} 
+2

bir dosyaya tampon tercih olacaktır (dolmasını engellemek hafıza -> büyük yanıtlar vermeniz gerekiyorsa). Ama burada senin fikrini görüyorum. – tuga

+0

[işte tam bir örnek filtre] (https://stackoverflow.com/a/46122629/26510), yine de bir "ByteArrayOutputStream" kullanarak bu çözüm için, ama düşmesi ve denemek ... fyi .... –

2

, senin inputStream bir yerel dosya sisteminden okunduğu takdirde, sadece eklenti:

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

daha net bir açıklama için tam kodu kontrol edin:

@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(); 
    } 
} 

istemci taraf Apache HttpClient kodudur.

2

ResourceConfig'i genişleten sınıfta, arabellek boyutunu ayarlayabilirsiniz. Bu boyutun üzerindeki yanıtlar yığınlanacak, aşağıda İçerik Uzunluğu olacaktır.

public class ApplicationConfig extends ResourceConfig { 

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

StackOverflow'daki çok benzer bir sorunuza yanıt can be found here

Ben Bir yoruma dönüştürülmemiş emin olmak için burada kopyaladıktan:

Bunu yapmak için büyük bir örnek filtresi, Projeden bağımsız olarak kullanılabilen, github üzerindeki Carrot2 projesinden this ContentLengthFilter.java. bir bayt akışı içeren bir cevap sarmalayıcı kullanır

Not sorunu çözmek için, bu nedenle bu da Transfer-Encoding: Chunked filtre zincirinde diğer bazı Filtre/kod tarafından belirlenen almaz sağlar ve ayarlı iken sizin Content-Length başlığını geçersiz kılar. Bunu, normalde yanıtta yer alan büyük dosyalar ile test ederek doğrulayabilirsiniz.

Ben kırık bir bağlantı haline gelmez sağlamak için, burada hem dosyanın içeriğini kopyalamak için gidiyorum:

/* 
* 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 
    } 
}