Send and receive multipart/form-data parts in Jakarta Restful Web Services
You can send different types of file data in a single HTTP request by using the multipart/form-data
content type. Jakarta RESTful Web Services (formerly JAX-RS) resources that run on Open Liberty can send and receive multipart/form-data
parts to and from clients.
In Jakarta Restful Web Services 3.0 and earlier, support for sending and receiving multipart/form-data parts was provided by the Liberty-specific IAttachment
and IMultipartBody
APIs, which are deprecated in version 3.1, which is included in Liberty 23.0.0.3. In this version and later, this support is provided by the EntityPart
API that is defined in the RESTful Web Services specification. For more information, see section 3.5.2 of the Jakarta Restful Web Services specification.
The following information applies only to Jakarta Restful Web Services 3.0 and earlier.
The multipart/form-data
content type is often used for form data in web applications. This content type can send multiple attachments, which are called parts, as a multipart payload in a single HTTP request. For example, a form that you use to upload a resume PDF file, a photo, and text for your name and address might send this content as multipart/form-data
parts. Jakarta Restful Web Services resources can implement the multipart/form-data
content type to receive such form data from a client. They can also use this content type to send multipart payloads back to a client in response to an HTTP request.
Prerequisites for sending and receiving multipart/form-data parts with Open Liberty
You can send and receive multipart/form-data
parts with Jakarta Restful Web Services resources by enabling the Jakarta RESTful Web Services feature and implementing the necessary code in your resource methods. However, you must also add a dependency to your Maven or Gradle project to make the required library available to your applications.
If you use Maven to build your applications, add the following dependency to your pom.xml
file.
<dependency>
<groupId>io.openliberty.api</groupId>
<artifactId>io.openliberty.jaxrs30</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
If you use Gradle, add the following dependency to your build.gradle
file.
buildscript {
repositories {
mavenCentral()
}
dependencies {
providedCompile 'io.openliberty.api:io.openliberty.jaxrs30:1.0'
}
}
Send multipart/form-data parts from Jakarta Restful Web Services resources
One way to send multipart form data in an HTTP request is to use an HTML form. You might create an HTML form for a human user to send file data to a web application. The following example shows an HTML form that sends an HTTP request in three parts that each specifies a particular content type.
<form action="http://www.example.com/" method="POST" enctype="multipart/form-data">
<input type="text" name="fileid" />
<br />
<input type="text" name="description" />
<br />
<input type="file" name="thefile" />
<br />
<input type="submit" name="submit" value="submit"/>
</form>
In this example, the type
attribute in the input
element specifies the content type for each part of the HTTP request.
However, if you want to automate the transfer of form data, you can write a Jakarta Restful Web Services client that sends a multipart payload. For example, you might create a microservice that automatically submits a purchase order when it receives a certain low inventory event. To send a multipart payload, you must send a list of IAttachment
objects. Each IAttachment
object in that list represents a single part in the multipart payload. In the following example, a Jakarta Restful Web Services client sends a POST
request to a remote service. The request has three parts: an XML file, an ID string for the file, and a text description of the file.
import com.ibm.websphere.jaxrs20.multipart.AttachmentBuilder;
import com.ibm.websphere.jaxrs20.multipart.IAttachment;
...
List<IAttachment> attachments = new ArrayList<>();
attachments.add(AttachmentBuilder.newBuilder("fileid")
.inputStream(new ByteArrayInputStream("person1234".getBytes()))
.build());
attachments.add(AttachmentBuilder.newBuilder("description")
.inputStream(new ByteArrayInputStream("XML file about person1234".getBytes()))
.build());
File file = new File("/path/to/multipart-response/person.xml");
attachments.add(AttachmentBuilder.newBuilder("thefile")
.inputStream("person.xml",new FileInputStream(file))
.contentType(MediaType.APPLICATION_XML_TYPE)
.build());
Client c = ClientBuilder.newClient();
WebTarget target = c.target("http://localhost:9080/myMultipartApp/person");
Response r = target.request(MediaType.TEXT_PLAIN)
.header("Content-Type", "multipart/form-data")
.post(Entity.entity(attachments, MediaType.MULTIPART_FORM_DATA_TYPE));
if(r.getStatus()==200 && "test".equals(r.readEntity(String.class)) {
System.out.println("multipart upload succeeded");
}
In this example, the IAttachment
list contains attachments
objects for the XML file, ID, and description. These objects form the parts of the multipart payload. Each part implements an InputStream
object to represent the data that is being transferred as an ordered stream of bytes.
Normally, you must close an InputStream
object by calling the InputStream close()
method. However, if you are sending multipart payloads in Open Liberty, do not close the InputStream
object. Open Liberty closes it for you after the data is sent. The server throws an IOException
error if the stream is closed beforehand.
Receive multipart/form-data parts with Jakarta Restful Web Services resources
To receive a multipart payload, a Jakarta Restful Web Services resource must implement a method to receive the multipart/form-data
parts of an HTTP request, as shown in the following example.
import com.ibm.websphere.jaxrs20.multipart.IAttachment;
...
@POST
@Consumes("multipart/form-data")
public Response postFormData(List <IAttachment> attachments) throws IOException{
InputStream stream = null;
for (IAttachment attachment : attachments) {
if (attachment == null) {
continue;
}
String contentType = attachment.getDataHandler().getContentType();
System.out.println("Content-type: " + contentType);
String fileName = attachment.getDataHandler().getName();
if (fileName == null) {
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(stream));
String line = null;
try {
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("Non-file attachment value: " + sb.toString());
} else {
File tempFile = new File(fileName);
System.out.println("File: " + tempFile.getAbsolutePath());
}
if (stream != null) {
stream.close();
}
}
return Response.ok("test").build();
}
With this configuration, Open Liberty can pass a List<IAttachment>
instance to your resource method so that you can process each part of the multipart payload. How you process the list of IAttachment
depends on the needs of your application. In this example, the postFormData
method iterates over each part and checks to see whether that part contains a file or not. It then prints the content type and contents of the part to the console output (System.out
).
When your application receives a multipart payload, Open Liberty does not close the InputStream
object because the server does not know when the application is done processing it. You must explicitly close the object in your application code. In the previous example, the stream.close
method closes the InputStream
object individually for each part of the multipart payload.