Multipart API in Jersey was made available by Craig in the recent 1.0.1 release. This API provides a simple mechanism to read and write body parts from within a restful application. Lets walk through a simple project resource which processes two parts, one JAXB bean and other an image.

Add the following jersey maven dependencies for running this sample.

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
            <version>1.0.1</version>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>1.0.1</version>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-multipart</artifactId>
            <version>1.0.1</version>
        </dependency>

        <dependency>
            <groupId>com.sun.grizzly</groupId>
            <artifactId>grizzly-servlet-webserver</artifactId>
            <version>1.9.0</version>
            <scope>test</scope>
        </dependency>

ProjectResource is fairly straight forward. It receives the body parts and processes them. Stores the image in file system. This resource consumes “multipart/mixed” type as there can be body parts with different media types sent in the multipart.

import com.sun.jersey.multipart.BodyPartEntity;
import com.sun.jersey.multipart.MultiPart;
import jersey.multipart.demo.model.Project;

import javax.imageio.ImageIO;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.util.UUID;

@Path("/project")
public class ProjectResource {


  @POST
  @Consumes("multipart/mixed")
  public Response post(MultiPart multiPart) {
    // First part contains a Project object
    Project project = multiPart.getBodyParts().get(0).getEntityAs(Project.class);
    System.out.println("name : " + project.getName());
    System.out.println("description : " + project.getDescription());
    System.out.println("license : " + project.getLicense());
    System.out.println("SVN URL : " + project.getSvnURL());
    System.out.println("homepage : " + project.getHomepage());

    // get the second part which is the project logo
    BodyPartEntity bpe = (BodyPartEntity) multiPart.getBodyParts().get(1).getEntity();
    String id = UUID.randomUUID().toString();
    boolean isProcessed = false;
    String message = null;
    try {
      InputStream source = bpe.getInputStream();
      BufferedImage bi = ImageIO.read(source);

      File file = new File(".\\\received\\\" + id + ".png");

      //storing the image to file system.
      if (file.isDirectory()) {
        ImageIO.write(bi, "png", file);
      } else {
        file.mkdirs();
        ImageIO.write(bi, "png", file);
      }
      isProcessed = true;

    } catch (Exception e) {
      message = e.getMessage();
    }
    if (isProcessed) {
      return Response.status(Response.Status.ACCEPTED).entity("Attachements processed successfully.").type(MediaType.TEXT_PLAIN).build();
    }

    return Response.status(Response.Status.BAD_REQUEST).entity("Failed to process attachments. Reason : " + message).type(MediaType.TEXT_PLAIN).build();
  }

}

The client code is simple enough. The multipart is a collection of body parts and it each body part should be associated with its media type. In our case, the first part is of “application/xml” since its a JAXB bean and the second part is of type “application/octet-stream” which is a PNG image. After constructing the multipart, you can post the data to the JAX-RS resource using the Jersey client API.

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.multipart.BodyPart;
import com.sun.jersey.multipart.MultiPart;
import jersey.multipart.demo.model.Project;

import javax.imageio.ImageIO;
import javax.ws.rs.core.MediaType;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.net.URL;

public class ProjectClient {
  public static void main(String[] args) throws Exception {
    final String BASE_URI = "http://localhost:9998/";

    Client c = Client.create();
    WebResource service = c.resource(BASE_URI);

    Project project = new Project();
    project.setName("Jersey");
    project.setDescription("Jersey is the open source, production quality, JAX-RS (JSR 311) Reference Implementation for building RESTful Web services.");
    project.setHomepage("https://jersey.dev.java.net/");
    project.setLicense("dual CDDL+GPL license");
    project.setSvnURL("https://jersey.dev.java.net/svn/jersey/trunk");

    ByteArrayOutputStream bas = new ByteArrayOutputStream();
    URL url = new URL("https://jersey.dev.java.net/images/Jersey_yellow.png");
    BufferedImage bi = ImageIO.read(url);
    ImageIO.write(bi, "png", bas);
    byte[] logo = bas.toByteArray();

    // Construct a MultiPart with two body parts
    MultiPart multiPart = new MultiPart().
      bodyPart(new BodyPart(project, MediaType.APPLICATION_XML_TYPE)).
      bodyPart(new BodyPart(logo, MediaType.APPLICATION_OCTET_STREAM_TYPE));

    // POST the request
    ClientResponse response = service.path("/project").
      type("multipart/mixed").post(ClientResponse.class, multiPart);
    System.out.println("Response Status : " + response.getEntity(String.class));
  }
}

The server code uses Grizzly container to deploy the Jersey resource and it enables easier testing. Make sure the grizzly project dependency is added to the POM in order to run this code.

import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class Server {
  public static void main(String[] args) throws IOException {
    final String baseUri = "http://localhost:9998/";
    final Map<String, String> initParams = new HashMap<String, String>();
    initParams.put("com.sun.jersey.config.property.packages",
      "jersey.multipart.demo.resources");
    System.out.println("Starting grizzly...");
    SelectorThread threadSelector = GrizzlyWebContainerFactory.create(baseUri, initParams);
    System.out.println(String.format("Jersey app started with WADL available at %sapplication.wadl\n"
      + "Try out %sproject\nHit enter to stop it...", baseUri, baseUri));
    System.in.read();
    threadSelector.stopEndpoint();
    System.exit(0);
  }
}

Handling multiparts in restful resources comes handy for certain web applications and Jersey provides this neat piece of integration as part of the JAX-RS RI.

Jersey rocks 🙂

Possibly Related Posts:



Arul

I am currently employed with Flux Corporation, Houston. This blog does not reflect the position or opinion of my employer. I'll blog my experience with things I'm fascinated with software and programming.

14 Comments

juliusdev · January 29, 2009 at 4:51 am

Great article ! But how to create a web method that returns the image file ? The response builder can return fileoutstream ?

Arul · February 1, 2009 at 2:03 pm

Hi juliusdev,

Glad to hear this article was useful to you.

You can return the InputStream in the Response from your web method.
For example, you could define your web method something like:

@GET
public Response getImageAsResponse() {
File file = new File(“.\\received\\” + “Jersey_yellow.png”);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return Response.ok(in).build();
}

On the client side, you could have the following code to process the image:

ClientResponse response = service.path(“/project”).get(ClientResponse.class);
BufferedImage bi = ImageIO.read(response.getEntity(InputStream.class));
File file = new File(“.\\received\\” + “test.png”);
ImageIO.write(bi, “png”, file);

Or, you could directly return the InputStream from your web method :

@GET
public InputStream getImageAsInputStream() {
File file = new File(“.\\received\\” + “Jersey_yellow.png”);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return in;
}

which could be processed on the client as :

InputStream in = service.path(“/project”).get(InputStream.class);
BufferedImage bi = ImageIO.read(in);

Let me know if this is what you were looking for.

-Arul

adrian.f · July 13, 2009 at 1:33 am

Very informative thanks!
A few questions:
1) I get 415 responses from my server with my test code. I’m using the multipart/mixed type. Could I be missing something else in my configuration?
1) Is it possible to stream the contents of a file within a BodyPart so that its contents do not need to be stored in a byte array prior to making the POST.
2) Could you embed the file’s name in the bodypart header so that the filename is maintained on the server side?

thanks very much.

adrian.f · July 13, 2009 at 9:38 pm

Please disregard my comment on the 415 response. Thanks.

Arul · July 19, 2009 at 1:16 pm

Hi Adrian,

I am glad this entry was useful to you. You may want to look into this thread which has code samples related to streaming content using Jersey Multipart API.

Cheers,
Arul

Abhijeet Mane · August 21, 2009 at 1:31 am

Hello Arul,
The content is very useful.But i have a problem while uploading the image from the client to the server(Restful web service).I have downloaded jersey-multipart-1.1.0-ea-SNAPSHOT.jar but still while deploying the above code it says Multipart class not found.
I am developing this Restfulweb service using Netbean IDE in tomcat

Waiting for your Reply.

Thanks,
Abhijeet Mane.

Arul · August 21, 2009 at 8:12 pm

Hi Abhijeet,

I am glad you found this entry useful. It would be much easier if you run this sample using Maven. There are few dependencies which gets pulled from POM. In this case, you are missing mail.jar which has the Multipart class.

HTH,
Arul

Abhijeet Mane · August 22, 2009 at 2:07 am

Hello Arul,

Thanks for your reply,I added the mail.jar file but i get a error saying No class definition “com/sun/jersey/core/util/StringKeyStringValueIgnoreCaseMultivaluedMap” when i run the client .I have written the client same as you have done.Please suggest as i have to deliver the functionality asap

Waiting for your reply.

Thanks
Abhijeet Mane.

Arul · August 22, 2009 at 10:21 am

Hi Abhijeet,

You are missing jersey-core-1.1.0-ea.jar this time. Make sure you add all the Jersey core, Jersey multipart and its dependent libraries.

Cheers,
Arul

ravichandrab4u · May 5, 2010 at 6:31 am

Hi,

I have tried the above example but stuck with an error. Jersey Client is not able to find the resource with multipart as mime type.
Pls help me…

Arul · May 12, 2010 at 9:16 pm

Could you post the stack trace you are seeing?

Did you have the multipart dependencies in your class path? mimepull.jar is required for Jersey multipart to work. If you use maven, this should get sucked in if you include the jersey-multipart dependency. For non-maven projects, you need to include it manually. See the Jersey documentation for more details here

-Arul

Nikhil · May 15, 2010 at 12:04 am

Hi

I am a non maven developer. Please let me know how do we get the Project class ? (Which jar file(s) are required ? I mean any other dependencies?)

Arul · May 18, 2010 at 9:31 pm

Hi Nikhil,

This is covered in quite detail in the Jersey User Guide.

-Arul

» Handling multiparts in Restful applications using CXF Blogging at the speed of thought: Life, Technology and More · February 5, 2009 at 2:08 am

[…] I will be reusing the same example which I used in my earlier blog entry for describing Jersey multiparts. […]

Comments are closed.