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:


14 Responses to “Handling multiparts in Restful applications using Jersey”

  • juliusdev

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

  • Arul

    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

    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.

  • Arul

    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

    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

    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

    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

    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

    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

    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

    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?)

Trackbacks/Pingbacks

  1.  » Handling multiparts in Restful applications using CXF Blogging at the speed of thought: Life, Technology and More