How to stream an endless InputStream with JAX-RS
Asked Answered
C

4

11

I have an endless InputStream with some data, which I want to return in response to a GET HTTP request. I want my web/API client to read from it endlessly. How can I do it with JAX-RS? I'm trying this:

@GET
@Path("/stream")
@Produces(MediaType.TEXT_PLAIN)
public StreamingOutput stream() {
    final InputStream input = // get it
    return new StreamingOutput() {
        @Override
        public void write(OutputStream out) throws IOException {
            while (true) {
                out.write(input.read());
                out.flush();
            }
        }
    };
}

But content doesn't appear for the client. However, if I add OutputStream#close(), the server delivers the content at that very moment. How can I make it truly streamable?

Chanson answered 18/4, 2013 at 19:57 Comment(5)
I've only used it for finite files, but perhaps you could try producing MediaType.APPLICATION_OCTET_STREAMEfferent
tried that, no effect :(Chanson
Which JAX-RS implementation are you using?Jonna
I'm using Jersey 1.17.1Chanson
There is a nice example now at dzone.com/articles/jax-rs-streaming-responseDett
S
5

Simply use the StreamingOutput of JAX-RS

@Path("/numbers")
public class NumbersResource {

    @GET
    public Response streamExample(){
        StreamingOutput stream = new StreamingOutput() {
            @Override
            public void write(OutputStream out) throws IOException, WebApplicationException {
                Writer writer = new BufferedWriter(new OutputStreamWriter(out));
                for (int i = 0; i < 10000000 ; i++){
                    writer.write(i + " ");
                }
                writer.flush();
            }
        };
        return Response.ok(stream).build();
    }
}
Sunn answered 26/8, 2020 at 21:23 Comment(1)
Thank you for this answer. It really helped me. The method signature of Response.ok(Object entity) is so unhelpful and misleading. It is unbelievable to me that this "modern" API from 2007 uses Object. And, the Javadoc gives no hint about StreamingOutput. Simply dreadful.Closemouthed
A
2

So, you have flush issues, you could try to get the ServletResponse as the spec says:

The @Context annotation can be used to indicate a dependency on a Servlet-defined resource. A Servlet- based implementation MUST support injection of the following Servlet-defined types: ServletConfig, ServletContext, HttpServletRequest and HttpServletResponse.

An injected HttpServletResponse allows a resource method to commit the HTTP response prior to returning. An implementation MUST check the committed status and only process the return value if the response is not yet committed.

Then flushing everything you can, like this:

@Context
private HttpServletResponse context;

@GET
@Path("/stream")
@Produces(MediaType.TEXT_PLAIN)
public String stream() {
    final InputStream input = // get it
    ServletOutputStream out = context.getOutputStream();
            while (true) {
                out.write(input.read());
                out.flush();
                context.flushBuffer();
            }
    return "";
}
Amygdalate answered 27/6, 2013 at 3:39 Comment(0)
A
0

Just a wild guess:

@GET
@Path("/stream")
@Produces(MediaType.TEXT_PLAIN)
public Response stream() {
    final InputStream input = getit();
    return Response.ok(input, MediaType.TEXT_PLAIN_TYPE).build();        
}
Amygdalate answered 26/6, 2013 at 0:19 Comment(2)
As explained above, InputStream will be read entirely into memory first, and then delivered to the client. This is not what I'm looking forChanson
Trying to use Response was a far guess, I thought it would flush this way.Amygdalate
C
-1

Folks should be using Java 9 or later can use transferTo to copy the input stream to the output stream so do this:


@GET
@Path("/stream")
@Produces(MediaType.TEXT_PLAIN)
public StreamingOutput stream() {
    final InputStream input = // get it

        StreamingOutput stream = output -> {
            try {
                is.transferTo(output);
            }
            catch (Exception e) {
                throw new WebApplicationException(e);
            } finally {
                is.close();
            }
        };

        return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM).build();
    }
Coreen answered 11/3, 2022 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.