web-dev-qa-db-fra.com

jersey - StreamingOutput en tant qu'entité de réponse

J'avais implémenté la sortie en streaming dans ma classe Jersey Resource.

@GET
@Path("xxxxx")
@Produces(BulkConstants.TEXT_XML_MEDIA_TYPE})   
public Response getFile() {

    FeedReturnStreamingOutput sout = new FeedReturnStreamingOutput();
    response = Response.ok(sout).build();
    return response;
}

class FeedReturnStreamingOutput implements StreamingOutput {

    public FeedReturnStreamingOutput()

    @Override
    public void write(OutputStream outputStream)  {
        //write into Output Stream
    }
}

Le problème persiste même si une réponse est renvoyée depuis la ressource avant que FeedReturnStreamingOutput ne soit appelé client Jersey attend que l'exécution de FeedReturnStreamingOutput soit terminée.

Code client:

Client client = Client.create();

ClientResponse response = webResource
    //headers
    .get(ClientResponse.class);

//The codes underneath executes after FeedReturnStreamingOutput is executed which undermines the necessity of streaming

OutputStream os = new FileOutputStream("c:\\test\\feedoutput5.txt");
System.out.println(new Date() + " : Reached point A");

if (response.getStatus() == 200) {
    System.out.println(new Date() + " : Reached point B");
    InputStream io = response.getEntityInputStream();

    byte[] buff = new byte[1024000];
    int count = 0;

    while ((count = io.read(buff, 0, buff.length)) != -1) {
        os.write(buff, 0, count);
    }

    os.close();
    io.close();

} else {
    System.out.println("Response code :" + response.getStatus());
}

System.out.println("Time taken -->> "+(System.currentTimeMillis()-startTime)+" ms");
19
PrabhaT

Le problème est la mise en mémoire tampon OutputStream que Jersey utilise pour mettre en mémoire tampon l'entité afin de déterminer l'en-tête Content-Length. La taille de la mémoire tampon par défaut est de 8 Ko. Vous pouvez désactiver la mise en mémoire tampon si vous le souhaitez ou simplement modifier la taille de la mémoire tampon avec la propriété 

ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER

Valeur entière qui définit la taille de la mémoire tampon utilisée pour mettre en mémoire tampon l'entité de réponse côté serveur afin de déterminer sa taille et de définir la valeur de l'en-tête HTTP "Content-Length".

Si la taille de l'entité dépasse la taille de la mémoire tampon configurée, la mise en mémoire tampon est annulée et la taille de l'entité n'est pas déterminée. Une valeur inférieure ou égale à zéro désactive la mise en mémoire tampon de l'entité.

Cette propriété peut être utilisée côté serveur pour remplacer la valeur de la taille du tampon des messages sortants - par défaut ou la valeur personnalisée globale définie à l'aide de la propriété globale "jersey.config.contentLength.buffer".

La valeur par défaut est 8192.

Voici un exemple

@Path("streaming")
public class StreamingResource {

    @GET
    @Produces("application/octet-stream")
    public Response getStream() {
        return Response.ok(new FeedReturnStreamingOutput()).build();
    }

    public static class FeedReturnStreamingOutput implements StreamingOutput {

        @Override
        public void write(OutputStream output)
                throws IOException, WebApplicationException {
            try {
                for (int i = 0; i < 10; i++) {
                    output.write(String.format("Hello %d\n", i).getBytes());
                    output.flush();
                    TimeUnit.MILLISECONDS.sleep(500);
                }
            } catch (InterruptedException e) {  throw new RuntimeException(e); }
        }
    }
}

Voici le résultat sans définir la propriété

 enter image description here

Et voici le résultat après avoir défini la valeur de la propriété sur 0

public class AppConfig extends ResourceConfig {
    public AppConfig() {
        ...
        property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0);
    }
}

 enter image description here

31
Paul Samsotha

Soit votre réponse est trop petite et ne reçoit jamais chunked afin que le serveur vide la requête entière en une fois. Ou vous avez un problème côté serveur où votre bibliothèque jax-rs attend d'avoir le flux complet avant le vidage.

Cependant, cela ressemble plus à un problème de client. Et vous semblez utiliser une ancienne version de jersey-client. 

De plus, .get(ClientResponse.class) semble louche.

Essayez d’utiliser le standard JAX-RS tel qu’il est aujourd’hui ( du moins dans le client ):

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target("http://localhost:8080/");
Response response = target.path("path/to/resource").request().get();

Avec le client maillot 2.17 dans le classpath:

<dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>2.17</version>
</dependency>
1
Daniel Sperry

Essayez d’appeler outputStream.flush() à partir de la méthode FeedReturnStreamingOutput.write(...) tous les X octets écrits dans le flux de sortie ou quelque chose du genre. 

Je suppose que le tampon de la connexion n'est pas rempli avec les données que vous renvoyez. Le service ne renvoie donc rien jusqu'à ce que Jersey appelle outputStream.close().

Dans mon cas, j’ai un service qui diffuse des données en continu et je le fais exactement comme vous: en renvoyant Response.ok(<instance of StreamingOutput>).build();.

Mon service renvoie les données d'une base de données et j'appelle outputStream.flush() après avoir écrit chaque ligne dans le flux de sortie.

Je sais que le service diffuse des données parce que je vois que le client commence à recevoir des données avant que le service n'ait fini d'envoyer le résultat complet.

1
Montecarlo

Je viens de me rendre compte que si vous utilisez une sous-classe de StreamingOutput, Jersey ne diffuse pas la réponse

// works fine
Response.ok(new StreamingOutput() { ... }).build();

// don't work
public interface MyStreamingOutput extends StreamingOutput { }
Response.ok(new MyStreamingOutput() { ... }).build();

Est-ce un bug de Jersey?

0
Mike Dias