- Is it possible to send an ouput stream as response to a HTTP request?
- 1 Answer 1
- Low Level Streaming with JAX-RS StreamingOutput
- JAX-RS StreamingOutput Example
- Demo
- References
- StreamingOutput in jax-rs usecases?
- 1 Answer 1
- JAX-RS: REST Streaming Response
- Example of using StreamingOutput as Response entity in Jersey
- 1 Answer 1
Is it possible to send an ouput stream as response to a HTTP request?
You don’t «send» an output stream. A server writes bytes to its HTTP response output stream, so that the client can receive the bytes in its connection’s input stream. An Output stream is like a keyboard: you use it to write things. An InputStream is like a screen: you use it to read things. But you don’t send a keyboard to someone to say something. You type it on the keyboard.
Thanks a lot JB Nizet 🙂 Though we have variables (of different data types) to read/write data in memory then why do we go for streams? Please help me know this.
A stream is rarely used to write things in memory. It’s mainly used to write things to the disk, or to the network. If you have two servers, even on the same machine, they don’t share the same memory. A variable stored in one process is not accessible from the other process. So you need to communicate between them by sending messages over the network. To continue with metaphors: you can write a note on a post-it for your wife who lives in your house. But to tell something to me, you need to send messages over the network.
Wow !! Very nice Explanation JB Nizet. Very much satisfied 🙂 But I still need a few clarifications 1. Is there any other thing similar to stream for communication over a network? 2. Have you refered to the http request and response as a network here? Then coming to my initial post — we cannot create an encrypted output stream at Server B(which is for reading and writing the data) since it has to be done only in Server A (which is meant for encryption — which would provide me an encrypted output stream for writing). So will it be any other way around. Kindly help me.
http is a network protocol. It consists in a client sending a request, over the network, to a server, and the server responding to that request with a response, over the network. I’m not sure I understand what you’re trying to achieve. But I have the feeling it’s much too complex for the skills you have now. Start by learning the basics.
1 Answer 1
Yes, it’s possible. That’s how the file download works.
First you’ll have to set the Content-Type that you’re going to provide. If it’s simple binary file then set it as application/octet-stream . After that get outputStream of the response and dump the file content in it.
response.setContentType("application/octet-stream"); response.setContentLength(fileSizeInBytes);//new File('myfile').length(), optional step response.getOutputStream().write(fileBytes);//do it in chunks
Streams are endpoints of data channels. Like an HTTP address is an endpoint to the server resource.
In case of stream our program doesn’t need to know where the actual resource resides. I just need to know how to interact with the stream.
So in case of HttpServletResponse stream, data lies in your server. Client’s browser (or any other client) establishes a connection with your server. When we call methods on the stream like read/write, data over this connection is sent or received. These calls in case of HttpServletResponse result in HTTP packet transfer over TCP connection.
For more information on Java Stream (or any other language with similar concept) check here.
Low Level Streaming with JAX-RS StreamingOutput
When working with large data or files, it is recommended to stream the output rather than loading the entire response into memory. This helps protect the servers resources and avoids getting out of memory exceptions. The JAX-RS StreamingOutput class is a simple callback that can be implemented to send the entity in the response when the application wants to stream the output.
JAX-RS StreamingOutput Example
We create a new StreamingOutput using an anonymous inner class and override the write() method. Using this method we can write directly to the OutputStream . This example streams all numbers from 1 to 9999999. When this service is called, it’ll stream the response when the output stream is available.
package com.memorynotfound.rs; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.io.*; @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(); > >
Demo
References
StreamingOutput in jax-rs usecases?
JAX-RS provides StreamingOutput interface that we can implement to do raw streaming of our response bodies.
public interface StreamingOutput
I am not sure as to why they go about building an interface to expose the response outputstream. Why not just inject an OutputStream directly and we can simply write on to it!!
1 Answer 1
In the book RESTful Java with JAX-RS 2.0 written by Bill Burk (one of RESTEasy authors), you will find a good explanation about StreamingOutput .
The same question you asked is answered by the author:
StreamingOutput is a simple callback interface that you implement when you want to do raw streaming of response bodies [. ]
You allocate implemented instances of this interface and return them from your JAX-RS resource methods. When the JAX-RS runtime is ready to write the response body of the message, the write() method is invoked on the StreamingOutput instance. [. ]
You may be asking yourself, “Why not just inject an OutputStream directly? Why have a callback object to do streaming output?” That’s a good question! The reason for having a callback object is that it gives the JAX-RS implementation freedom to handle output however it wants. For performance reasons, it may sometimes be beneficial for the JAX-RS implementation to use a different thread other than the calling thread to output responses. More importantly, many JAX-RS implementations have an interceptor model that abstracts things out like automatic GZIP encoding or response caching. Streaming directly can usually bypass these architectural constructs. Finally, the Servlet 3.0 specification has introduced the idea of asynchronous responses. The callback model fits in very nicely with the idea of asynchronous HTTP within the Servlet 3.0 specification.
And the StreamingOutput documentation states the following:
JAX-RS: REST Streaming Response
In this post, we are going to see how to streaming a response with JAX-RS StreamingOutput.
StreamingOutput is an interface(https://docs.oracle.com/javaee/7/api/javax/ws/rs/core/StreamingOutput.html) and used as a resource method return value or as the entity in a Response when the application wishes to stream the output.
Consider that we have a list of person objects and each object contains first and last name and we want to create a RESTful web service to fetch the person details. Our goal is to stream the response instead of getting everything. Lets see how we can use StreamingOutput to achieve our goal.
The below is Person model class which contains two fields such as first name and last name.
package com.resource; public class Person < private String firstName; private String lastName; public String getFirstName() < return firstName; >public void setFirstName(String firstName) < this.firstName = firstName; >public String getLastName() < return lastName; >public void setLastName(String lastName) < this.lastName = lastName; >>
The below is the Resource class and the method getDetails passes StreamingOutput to streamPersonData method of PersonService class.
Produces is “application/octet-stream” because we want to save the response as a file instead of rendering that in the browser.
package com.resource; import com.service.PersonService; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; @Path("/person") public class PersonResource < @GET @Produces("application/octet-stream") @Path("details") public Response getDetails() < return Response.ok((StreamingOutput) new PersonService()::streamPersonData).build(); >>
The below is the PersonService class which contains the necessary logic to get the person details and convert the person object into JSON format and then write the converted JSON to OutputStream.
Because we are writing the output as JSON array, we are adding a comma symbol at the end of each person JSON output.
package com.service; import com.model.Person; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; public class PersonService < private ObjectMapper objectMapper = new ObjectMapper(); public void streamPersonDetails(OutputStream output) throws IOException < output.write('['); int i = 1; List personDetails = getPersonDetails(); for (Person person : personDetails) < output.write(objectMapper.writeValueAsBytes(person)); if (i < personDetails.size()) < output.write(','); >i++; > output.write(']'); > private List getPersonDetails() < List personList = new ArrayList(); for (int i = 0; i < 500; i++) < Person person = new Person(); person.setFirstName("Peter:" + i); person.setLastName("Woods:" + i); personList.add(person); >return personList; > >
The output is given below,
If we want to render the response as “JSON” and view the response in browser, then we have to change the produces like this “@Produces(MediaType.APPLICATION_JSON)” and the output look like below,
Example of using StreamingOutput as Response entity in Jersey
Can someone post an example of how in Jersey to set StreamingOutput as an entity in a Response object? I haven’t been able to find an example of this.
1 Answer 1
@GET @Produces(MediaType.TEXT_PLAIN) public Response streamExample() < StreamingOutput stream = new StreamingOutput() < @Override public void write(OutputStream os) throws IOException, WebApplicationException < Writer writer = new BufferedWriter(new OutputStreamWriter(os)); writer.write("test"); writer.flush(); // >; return Response.ok(stream).build(); >
AAAAARRRRRRRRRRRRRRRRRRRRGH. I just spent four hours trying to figure out why my jersey rest service wasn’t streaming a file. I didn’t put a flush on my writer. Sooooooo annoying. Thank you for rescuing me!
Unfortunately, neither the JAX-RS V 1.1 spec nor the Java EE 6 Javadocs specify if the given output stream should be closed or not. Considering the preceeding comment though, I suspect you should indeed call OutputStream.close() instead of OutputStream.flush(). This may free some internal resources which are associated with the output stream.
@卢声远ShengyuanLu You can also use a MessageBodyWriter but this question was about streaming with a Response object. With MessageBodyWriter you’re obligated to write to an OutputStream .
Thanks for this answer, but how the heck do I get this StreamingOutput object out of the Response on the client side??
@prongs + Nagyl: Yeah, I figured it out: InputStream is = ClientBuilder.newBuilder().register(MultiPartFeature.class).build().target(BASE_URI).path(«/yourservice»).request().get().readEntity(InputStream.class); (all in one «sentence», it might be good to split this up into Client, Target and Response object)