Handling a multipart request
Table of contents
- Building a multipart request
- Sending a multipart request
- Receiving a multipart request
- Using type://@Param annotation
Armeria provides Multipart
encoder and decoder built on top of
Reactive Streams.
Building a multipart request
A Multipart
consists of multiple BodyParts
.
Each BodyPart
is divided into headers and a body. A BodyPart
headers can be created simply
using ContentDisposition
.
String
, HttpData
, or StreamMessage
can be set as the body of the BodyPart
.
import java.nio.file.Path;
import com.linecorp.armeria.common.ContentDisposition;
import com.linecorp.armeria.common.multipart.BodyPart;
// Create a 'Content-Disposition' header with the 'name' parameter set to 'name'.
ContentDisposition nameDisposition =
ContentDisposition.of("form-data", "name");
// Create a BodyPart with 'Content-Disposition' header and its data.
BodyPart bodyPart1 = BodyPart.of(nameDisposition, "Meri Kim");
// Create a 'Content-Disposition' header with the name parameter set to "image",
// and the filename parameter set to "profile.png"
ContentDisposition imageDisposition =
ContentDisposition.of("form-data", "image", "profile.png");
Path image = Paths.get("/path/to/image");
// Create a BodyPart with 'Content-Disposition' header and its file.
BodyPart bodyPart2 = BodyPart.of(imageDisposition, StreamMessage.of(image));
// Create a new multipart with the two body parts.
Multipart multipart = Multipart.of(bodyPart1, bodyPart2);
If we encode and print the Multipart
created above,
for (HttpData httpData : multipart.toStreamMessage().collect().join()) {
System.out.print(httpData.toStringUtf8());
}
we can see how the Multipart
is encoded as shown below:
--ArmeriaBoundaryEsbNVr9Z66DAIYIN
content-disposition:form-data; name="name"
content-type:text/plain
Meri Kim
--ArmeriaBoundaryEsbNVr9Z66DAIYIN
content-disposition:form-data; name="image"; filename="profile.png"
content-type:application/octet-stream
<binary-data>
--ArmeriaBoundaryEsbNVr9Z66DAIYIN--
Sending a multipart request
The Multipart
created in this way can be converted to an HttpRequest
through
Multipart.toHttpRequest()
and transmitted to a server using a WebClient
.
WebClient client = WebClient.of("https://armeria.dev");
// Encode a `Multipart` into an `HttpRequest`
HttpRequest request = multipart.toHttpRequest("/upload");
client.execute(request).aggregate()...;
Receiving a multipart request
On the server side, the multipart request sent from the client can be decoded into a Multipart
using Multipart.from()
.
Server.builder()
.service((ctx, req) -> {
// Decode an `HttpRequest` into a `Multipart`
Multipart multipart = Multipart.from(req);
...
})
Since Multipart
does not have the actual multipart data, you can use
Multipart.bodyParts().subscribe(...)
to read data little by little as needed.
If the size of the data is not large, the data can be read after being loaded into memory through
Multipart.aggregate()
.
// Use a `Subscriber` to read the data with backpressure.
multipart.bodyParts().subsribe(new Subscriber() {
...
});
// Read the data after aggregation.
Multipart.from(req).aggregate()
.thenAccept(multipart -> {
for (AggregatedBodyPart bodyPart : multipart.bodyParts()) {
String content = bodyPart.contentUtf8();
...
}
});
Using @Param
annotation
In annotated services, a body part content of multipart/form-data
can be directly mapped into a String
,
Path
, File
, or MultipartFile
using the @Param
annotation.
import java.io.File;
import java.nio.file.Path;
import com.linecorp.armeria.common.MediaTypeNames;
import com.linecorp.armeria.common.multipart.MultipartFile;
import com.linecorp.armeria.server.annotation.Consumes;
import com.linecorp.armeria.server.annotation.Post;
@Consumes(MediaTypeNames.MULTIPART_FORM_DATA)
@Post("/upload")
public HttpResponse upload(
@Param String param,
@Param File file,
@Param Path path,
@Param MultipartFile multipartFile) {
// Do something with the multipart data
...
}