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.wadln"
      + "Try out %sprojectnHit 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 :)