users@jersey.java.net

Serving Binary Content (Files) Through Jersey

From: James Allchin <james_at_knowledgemill.com>
Date: Fri, 24 Apr 2009 10:09:38 +0100

Hi All,

Thanks to everyone who keeps answering my questions on the forum. The
support is excellent.

My current question is how to serve binary content i.e. a file out through
my RESTful services.

I have managed to accept a file through my REST implementation with the
following method (it uses apache.commons upload) which is a nice combo in my
view (particularly streaming capabilities).

    @POST
    @Path("user/{userId}/artifacts/{artifactId}/content")
    @Consumes("multipart/form-data")
    //_at_Consumes("multipart/mixed")
    public void setArtifactBinaryContent(@PathParam("userId") long userId
                                       , @PathParam("artifactId") long
artifactId
                                       , @javax.ws.rs.core.Context
HttpServletRequest request)
       throws Exception {
            // First write the file to disk
            FileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            List items = null;
            try {
                items = upload.parseRequest(request);
                Iterator iter = items.iterator();
                while (iter.hasNext()) {
                    FileItem item = (FileItem)iter.next();
                    if (!item.isFormField()) {
                        File fileLocator = new File("/fileshare/filedata/" +
artifactId + ".dat");
                        item.write(fileLocator);
                    }
                }
                // Assuming success we now update the link and set to
enabled
                dA.updateArtifactLink(0, userId, artifactId,
"/fileshare/filedata/" + artifactId + ".dat");
                dA.updateArtifactEnabled(0, userId, artifactId, true);
            }
            catch (Exception e) {
                e.printStackTrace();
            }

    }

This works a treat... The problem I am having is now serving the file. I am
trying the following:

    @GET
    @Path("user/{userId}/artifacts/{artifactId}/content")
    @Produces("application/octet-stream")
    public void getArtifactBinaryContent(@PathParam("userId") long userId
                                       , @PathParam("artifactId") long
artifactId
                                       , @javax.ws.rs.core.Context
HttpServletResponse response)
       throws Exception {
            try {
                // Get an internal representation of an artifact
                InternalArtifact ia = dA.getInternalArtifact(0, userId,
artifactId);

                // Get the file from disk
                File f = new File(ia.getLink());
                int fLength = (int)f.length();

                // Set the response header header
                response.setContentType("application/octet-stream");
                response.setContentLength(fLength);
                response.setHeader( "Content-Disposition", "attachment;
filename=\"" + artifactId + "\"" );
                ServletOutputStream sos = response.getOutputStream();

                // Output the file stream
                byte[] bbuf = new byte[100];
                DataInputStream in = new DataInputStream(new
FileInputStream(f));
                int length = 0;
                while ((in != null) && ((length = in.read(bbuf)) != -1))
                {
                    sos.write(bbuf,0,length);
                }
                in.close();
                sos.flush();
                sos.close();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
    }

When deploying it moans with the following error:

SEVERE: Servlet /ACMERest threw load() exception
com.sun.jersey.api.container.ContainerException: Method, public void
com.acme.rest.MainService.getArtifactBinaryContent(long,long,javax.servlet.http.HttpServletResponse)
throws java.lang.Exception, annotated with GET of resource, class
com.acme.rest.MainService, is not recognized as valid Java method annotated
with @HttpMethod.

Does anyone know what the issue is here. Is it incorrect to be using
@Context to access the HttpServletResponse object?

Is there another way to serve a file perhaps?

Thanks,

James