users@jersey.java.net

Re: Serving Binary Content (Files) Through Jersey

From: James Allchin <james_at_knowledgemill.com>
Date: Fri, 24 Apr 2009 11:42:14 +0100

In the end I went for the following - I simply returned the File object:

    @GET
    @Path("user/{userId}/artifacts/{artifactId}/content")
    @Produces("application/*")
    public File getArtifactBinaryContent(@PathParam("userId") long userId
                                       , @PathParam("artifactId") long
artifactId
                                       )
       throws Exception {
            File f = null;
            try {
                // Get an internal representation of an artifact
                InternalArtifact ia = dA.getInternalArtifact(0, userId,
artifactId);

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

            }
            catch (Exception e) {
                e.printStackTrace();
            }
            return f;
    }

I am not sure that this particularly performant though as I suspect it will
put the whole file into memory and return the file content in one bang -
instead of streaming.

Anyone have any better thoughts for serving a file via Jersey?

Thanks

James


On Fri, Apr 24, 2009 at 10:42 AM, James Allchin <james_at_knowledgemill.com>wrote:

> OK - I suspect it is something to do with a VOID method instead of
> returning a type...
>
> How do I overcome this if I wish to serve via HttpServletResponse?
>
>
>
> On Fri, Apr 24, 2009 at 10:09 AM, James Allchin <james_at_knowledgemill.com>wrote:
>
>> 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
>>
>>
>>
>