Skip to content

Multipart & Forms

Methanol has special BodyPublisher implementations for multipart uploads & form submission.

Multipart Bodies

MultipartBodyPublisher implements the multipart format. A multipart body has one or more parts. Each part has a BodyPublisher for its content and HttpHeaders that describe it. MultipartBodyPublisher.Builderdefaults to multipart/form-data if a multipart MediaType isn't explicitly specified. There are special methods for adding parts with a Content-Disposition: form-data header generated from a field name and an optional file name. These are referred to as form parts.

// Substitute with your client ID. Visit https://api.imgur.com/oauth2/addclient to get one.
static final String CLIENT_ID = System.getenv("imgur.client.id"); 

final Methanol client = Methanol.create();

HttpResponse<String> uploadGif() throws IOException, InterruptedException {
  var multipartBody = MultipartBodyPublisher.newBuilder()
      .textPart("title", "Dancing stick bug")
      .filePart("image", Path.of("dancing-stick-bug.gif"), MediaType.IMAGE_GIF)
      .build();
  return client.send(
      MutableRequest.POST("https://api.imgur.com/3/image", multipartBody)
          .header("Authorization", "Client-ID " + CLIENT_ID), 
      BodyHandlers.ofString());
}

If filePart isn't given a MediaType, it asks the system for one using the given Path, falling back to application/octet-stream if that doesn't work.

Hint

A part's Content-Type is automatically added if it's created with a MimeBodyPublisher.

Generic Form Parts

Use builder's formPart to add a form part from an arbitrary BodyPublisher. It takes a field name and an optional file name.

// Substitute with your client ID. Visit https://api.imgur.com/oauth2/addclient to get one.
static final String CLIENT_ID = System.getenv("imgur.client.id");

final Methanol client = Methanol.create();

HttpResponse<String> uploadGif() throws IOException, InterruptedException {
  var multipartBody = MultipartBodyPublisher.newBuilder()
      .textPart("title", "Dancing stick bug")
      .formPart(
          "image", title + ".png", MoreBodyPublishers.ofMediaType(imagePart, MediaType.IMAGE_PNG))
      .build();
  return client.send(
      MutableRequest.POST("https://api.imgur.com/3/image", multipartBody)
          .header("Authorization", "Client-ID " + CLIENT_ID),
      BodyHandlers.ofString());
}

Tip

Use MoreBodyPublishers::ofMediaType to pair an arbitrary BodyPublisher with its proper MediaType if you want a Content-Type header to be specified by the part.

Form Bodies

Use FormBodyPublisher to send form data as a set of URL-encoded queries. Data is added as string name-value pairs.

final Methanol client = Methanol.create();

HttpResponse<String> sendQueries(String url, Map<String, String> queries)
    throws IOException, InterruptedException {
  var builder = FormBodyPublisher.newBuilder();
  queries.forEach(builder::query);
  return client.send(MutableRequest.POST(url, builder.build()), BodyHandlers.ofString());
}

Hint

Requests with MultipartBodyPublisher or FormBodyPublisher will have their Content-Type header added automatically if sent on a Methanol client.