Niranjan Borawake
Niranjan Borawake

Reputation: 1638

Jersey REST service to return response as an image with download window

I have a D3 graph generated on client side. I send the SVG data to Jersey REST service convert the SVG data to image (OutputStream) using Transcoding. When I return the response from service I could see the data returned as binary stream in console.

What I want is a browser download window to popup as soon as the service returns the response.

Following is the code snippet:

@POST
@Path("downloadSVG")
@Produces("image/png")

public javax.ws.rs.core.Response downloadSVG(@Context HttpServletRequest request, @Context HttpServletResponse httpServletResponse,String values){

              LOGGER.info("Inside downloadSVG service.");

              javax.ws.rs.core.Response graphImage = null;
              JSONObject data = new JSONObject(values);
              String svgXML = data.get("svgURL").toString();
              try {

                     OutputStream os = httpServletResponse.getOutputStream();
                     JPEGTranscoder t = new JPEGTranscoder();
                     t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(.8));
                     InputStream is = new ByteArrayInputStream(svgXML.getBytes());
                     TranscoderInput input = new TranscoderInput(is);
                     TranscoderOutput output = new TranscoderOutput(os);
                     t.transcode(input, output);
                     LOGGER.info("Graph image generated. Trying to return it to client.");
                     graphImage = javax.ws.rs.core.Response.ok(os).header("Content-Disposition", "attachment;       
                filename=graph.png").type("image/png").build();
             os.flush();
                     os.close();
              } catch (IOException e1) {
                     e1.printStackTrace();
              } catch (TranscoderException e) {
                     e.printStackTrace();
              }
              return graphImage;
}

Once this service is called I get following Exception on server console:

SLF4J: This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version
Jan 03, 2014 3:21:32 PM com.sun.jersey.spi.container.ContainerResponse write
SEVERE: A message body writer for Java class org.apache.catalina.connector.CoyoteOutputStream, and Java type class org.apache.catalina.connector.CoyoteOutputStream, and MIME media type image/png was not found
Jan 03, 2014 3:21:32 PM com.sun.jersey.spi.container.ContainerResponse write
SEVERE: The registered message body writers compatible with the MIME media type are:
image/* ->
  com.sun.jersey.core.impl.provider.entity.RenderedImageProvider
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider
  com.sun.jersey.core.impl.provider.entity.StringProvider
.
.
Jan 03, 2014 3:21:32 PM com.sun.jersey.spi.container.ContainerResponse logException
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message body writer for Java class org.apache.catalina.connector.CoyoteOutputStream, and Java type class org.apache.catalina.connector.CoyoteOutputStream, and MIME media type
image/png was not found
        at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:285)
        at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1448)
        at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1360)
.
.
.
Caused by: com.sun.jersey.api.MessageException: A message body writer for Java class org.apache.catalina.connector.CoyoteOutputStream, and Java type class org.apache.catalina.connector.CoyoteOutputStream, and MIME media type image/png was not found

... 56 more

I think I am missing some Jersey dependency.

Any pointers are highly appreciated. Thanks in advance.

Upvotes: 0

Views: 6125

Answers (1)

John R
John R

Reputation: 2106

I think I am missing some Jersey dependency.

No I don't think so... I believe the fundamental issue is that you can't directly manipulate the response OutputStream and return a Response from your downloadSVG method.

Any pointers are highly appreciated.

A cleaner approach would be to separate the logic that constructs the image from the logic that renders the image to the client. Check out MessageBodyWriter. It gives you access to the request OutputStream and allows you to manipulate the response headers.

Here's what I would do:

1) Create a new class (MyPNG). Modify your REST method to return MyPNG rather than Response and mark it with a @Produces tag.

@Produces("image/png")
public MyPNG downloadSVG(@Context HttpServletRequest request, @Context HttpServletResponse httpServletResponse,String values){

2) In your REST method, read the InputStream and create and return a new instance of MyPNG

3) Implement MessageBodyWriter and do something like this in the writeTo method:

public PNGMessageBodyWriter implements MessageBodyWriter<MyPNG> {
....
boolean isWriteable(java.lang.Class<?> type,
                java.lang.reflect.Type genericType,
                java.lang.annotation.Annotation[] annotations,
                MediaType mediaType)
{
    return mediaType.toString.equals("image/png");
}
....
void writeTo(MyJPEG instance, java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, 
    MediaType mediaType, MultivaluedMap<java.lang.String,java.lang.Object> httpHeaders, java.io.OutputStream entityStream) throws java.io.IOException,
                    WebApplicationException
{
    JPEGTranscoder t = new JPEGTranscoder();
    t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(.8));
    InputStream is = new ByteArrayInputStream(instance.getBytes());
    TranscoderInput input = new TranscoderInput(is);
    TranscoderOutput output = new TranscoderOutput(entityStream);
    t.transcode(input, output);
    httpHeaders.put("Content-Disposition", ""attachment;filename=" + instance.getFilename());
    // and so on....
    httpHeaders.put("whatever else", "some other value");
}

4) Follow the instructions appropriate for your version of Jersey to register your MessageBodyWriter as a Provider.

Upvotes: 2

Related Questions