2012-07-25 8 views
8

Sto utilizzando Jersey per creare risorse API RESTful e ResponseBuilder per generare la risposta.Jersey/JAX-RS: Return Content-Length nell'intestazione di risposta anziché codifica di trasferimento chunked

codice di esempio per la risorsa 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(); 
    } 
} 

Nella risposta, è di tornare codifica di trasferimento Chunked nelle intestazioni di risposta. Qual è il modo corretto nel "mondo Jersey" di restituire l'intestazione Content-Length anziché l'intestazione Transfer-Encoding: chunked nelle intestazioni di risposta?

risposta

4

Selezionare Content-Length o Transfer-Encoding è solo una scelta di contenitori. È davvero una questione di dimensioni del buffer.

Una possibile soluzione è fornire un SevletFilter che buffer tutti quei byte marshalled e imposta il valore di intestazione Content-Length.

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

Sarebbe preferibile di buffer in un file (per evitare di esaurire di memoria -> se hai bisogno di restituire grandi risposte). Ma vedo il tuo punto qui. – tuga

+0

[ecco un filtro di esempio completamente funzionante] (https://stackoverflow.com/a/46122629/26510) per questa soluzione, ancora utilizzando un 'ByteArrayOutputStream', ma facile da inserire e provare ... fyi .... –

2

Ad esempio, se l'InputStream viene letto da un file system locale, basta aggiuntivo:

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

controllare il codice completo per una spiegazione più chiara:

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

Il cliente lato è un codice Apache HttpClient.

2

Nella classe che estende ResourceConfig è possibile impostare la dimensione del buffer. Le risposte superiori a queste dimensioni saranno suddivise in blocchi, di seguito verrà visualizzato Content-Length.

public class ApplicationConfig extends ResourceConfig { 

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

una risposta ad una domanda molto simile su StackOverflow può be found here

ho copiato qui per assicurarsi che non è convertito in un commento:

Un grande filtro di esempio per fare questo, che può essere utilizzato standalone dal progetto, è this ContentLengthFilter.java dal progetto Carrot2 su github.

nota che utilizza un involucro risposta con un flusso di byte per risolvere il problema, quindi questo assicura anche che Transfer-Encoding: Chunked non viene impostata da un altro filtro/codice nella catena dei filtri, e ignorare l'intestazione Content-Length quando è impostato. Puoi verificarlo testandolo con file più grandi, dato che normalmente verrebbero suddivisi nella risposta.

ho intenzione di copiare il contenuto del file anche qui, al fine di garantire che non diventi un collegamento interrotto:

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