2012-07-25 8 views
8

Ich verwende Jersey, um RESTful API-Ressourcen zu erstellen, und , um die Antwort zu generieren.Jersey/JAX-RS: Rückgabe Content-Length in Antwort-Header anstelle von Chunked-Transfer-Codierung

Beispielcode für die RESTful Ressource:

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 der Antwort, die sie in den Antwort-Header segmentierte Übertragungscodierung zurück. Was ist der richtige Weg in der "Jersey Welt", um es die Content-Length Header anstelle der Transfer-Encoding: chunked Header in den Antwortheadern zurückgeben?

Antwort

4

Wählen Sie Content-Length oder Transfer-Encoding ist nur diese Container Wahl. Es ist wirklich eine Frage der Puffergröße.

Eine mögliche Lösung ist die Bereitstellung einer SevletFilter, die alle diese gemarshallt Bytes puffert und setzt Content-Length Header-Wert.

Siehe 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

Es wäre besser, in eine Datei zu puffern (out laufen zu vermeiden von Speicher -> wenn Sie große Antworten zurückgeben müssen. Aber ich sehe deinen Standpunkt hier. – tuga

+0

[hier ist ein voll funktionierendes Beispiel Filter] (https://Stackoverflow.com/a/46122629/26510) für diese Lösung, immer noch mit einem 'ByteArrayOutputStream', aber einfach hineinlegen und versuchen ... fyi .... –

2

Zum Beispiel, wenn Ihr Eingabestrom aus einem lokalen Dateisystem gelesen wird, nur add:

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

überprüfen Sie den vollständigen Code für eine deutlichere Erklärung:

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

Der Client Seite ist ein Apache HttpClient-Code.

2

In Ihrer Klasse, die ResourceConfig erweitert, können Sie die Puffergröße festlegen. Antworten über dieser Größe werden chunked, darunter wird Content-Length.

public class ApplicationConfig extends ResourceConfig { 

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

eine Antwort auf eine ganz ähnliche Frage auf Stackoverflow kann be found here

Ich habe es hier kopiert, um sicherzustellen, ist es nicht auf einen Kommentar umgewandelt:

Ein großer Probenfilter, dies zu tun, das kann unabhängig vom Projekt verwendet werden, ist this ContentLengthFilter.java aus dem Carrot2 Projekt auf GitHub.

Beachten Sie, dass ein Antwortwrapper mit einem Byte-Stream das Problem löst, so dass auch Transfer-Encoding: Chunked nicht durch einen anderen Filter/Code in der Filterkette gesetzt wird und überschreiben Ihre Content-Length-Header, wenn es festgelegt ist. Sie können dies überprüfen, indem Sie dies mit größeren Dateien testen, da diese normalerweise in der Antwort chunked werden.

Ich werde den Inhalt der Datei auch hier, kopieren, um sicherzustellen, es nicht zu einem Defekten 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 
    } 
} 
Verwandte Themen