[[_multipart]]
== Multipart Providers

RESTEasy has rich support for the "multipart/*" and "multipart/form-data" mime types.
The multipart mime format is used to pass lists of content bodies.
Multiple content bodies are embedded in one message.
"multipart/form-data" is often found in web application HTML Form documents and is generally used to upload files.
The form-data format is the same as other multipart formats, except that each inlined piece of content has a name associated with it. 

RESTEasy provides a custom API for reading and writing multipart types as well as marshalling arbitrary List (for any multipart type) and Map (multipart/form-data only) objects 

Classes `MultipartInput` and `MultipartOutput` provide read and write support for mime type "multipart/mixed" messages respectively.
They provide for multiple part messages, in which one or more different sets of data are combined in a single body. 

`MultipartRelatedInput` and `MultipartRelatedOutput` classes provide read and write support for mime type "multipart/related" messages.
These are messages that contain multiple body parts that are inter-related. 

`MultipartFormDataInput` and `MultipartFormDataOutput` classes provide read and write support for mine type "multipart/form-data".  This type is used when returning a set of values as the the result of a user filling out a form or for uploading files. 

[[_multipart_mixed_group]]
=== Multipart/mixed

[[_multipart_output]]
==== Writing multipart/mixed messages

`MultipartOutput` provides a set of addPart methods for registering message content and specifying special marshalling requirements.
In all cases the addPart methods require an input parameter, Object and a MediaType that declares the mime type of the object.
Sometimes there may be an object in which marshalling is sensitive to generic type metadata.
In such cases, use an addPart method in which you declare the GenericType of the entity Object.
Perhaps a file will be passed as content and it will require UTF-8 encoding.
Setting input parameter, utf8Encode to `true` will indicate to RESTEasy to process the filename according to the character set and language encoding rules of `rfc5987`.
This flag is only processed when mime type "multipart/form-data" is specified. 

`MultipartOutput` automatically generates a unique message boundary identifier when it is created.
Method setBoundary is provided to declare a different identifier. 

[source,java]
----

public class MultipartOutput {
   public OutputPart addPart(Object entity, MediaType mediaType);
   public OutputPart addPart(Object entity, MediaType mediaType,
        String filename);
   public OutputPart addPart(Object entity, MediaType mediaType,
        String filename, boolean utf8Encode);
   public OutputPart addPart(Object entity, GenericType<?> type,
        MediaType mediaType);
   public OutputPart addPart(Object entity, GenericType<?> type,
        MediaType mediaType, String filename);
   public OutputPart addPart(Object entity, GenericType<?> type,
        MediaType mediaType, String filename, boolean utf8Encode);
   public OutputPart addPart(Object entity, Class<?> type, Type genericType,
        MediaType mediaType);
   public OutputPart addPart(Object entity, Class<?> type, Type genericType,
        MediaType mediaType, String filename);
   public OutputPart addPart(Object entity, Class<?> type, Type genericType,
        MediaType mediaType, String filename, boolean utf8Encode);
   public List<OutputPart> getParts();
   public String getBoundary();
   public void setBoundary(String boundary);
}
----

Each message part registered with `MultipartOutput` is represented by an `OutputPart` object.
Class `MultipartOutput` generates an `OutputPart` object for each addPart method call. 

[source,java]
----
public class OutputPart {
   public OutputPart(final Object entity, final Class<?> type,
        final Type genericType, final MediaType mediaType);
   public OutputPart(final Object entity, final Class<?> type,
        final Type genericType, final MediaType mediaType,
        final String filename);
   public OutputPart(final Object entity, final Class<?> type,
        final Type genericType, final MediaType mediaType,
        final String filename, final boolean utf8Encode);
   public MultivaluedMap<String, Object> getHeaders();
   public Object getEntity();
   public Class<?> getType();
   public Type getGenericType();
   public MediaType getMediaType();
   public String getFilename();
   public boolean isUtf8Encode();
}
----

[[_multipartinput]]
==== Reading multipart/mixed messages

`MultipartInput` and `InputPart` are interface classes that provide access to multipart/mixed message data.
RESTEasy provides an implementation of these classes.
They perform the work to retrieve message data. 

[source,java]
----
package org.jboss.resteasy.plugins.providers.multipart;

import java.util.List;

public interface MultipartInput {
   List<InputPart> getParts();
   String getPreamble();
   /**
    * Call this method to delete any temporary files created from unmarshalling
    * this multipart message
    * Otherwise they will be deleted on Garbage Collection or JVM exit.
    */
   void close();
}
----

[source,java]
----
package org.jboss.resteasy.plugins.providers.multipart;

import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import java.io.IOException;
import java.lang.reflect.Type;

/**
 * Represents one part of a multipart message.
 */
public interface InputPart {
   /**
    * If no content-type header is sent in a multipart message part
    * "text/plain; charset=ISO-8859-1" is assumed.
    *
    * This can be overwritten by setting a different String value in
    * {@link org.jboss.resteasy.spi.HttpRequest#setAttribute(String, Object)}
    * with this ("resteasy.provider.multipart.inputpart.defaultContentType")
    * String as key. It should be done in a
    * {@link jakarta.ws.rs.container.ContainerRequestFilter}.
    */
   String DEFAULT_CONTENT_TYPE_PROPERTY =
    "resteasy.provider.multipart.inputpart.defaultContentType";

   /**
    * If there is a content-type header without a charset parameter,
    * charset=US-ASCII is assumed.
    *
    * This can be overwritten by setting a different String value in
    * {@link org.jboss.resteasy.spi.HttpRequest#setAttribute(String, Object)}
    * with this ("resteasy.provider.multipart.inputpart.defaultCharset")
    * String as key. It should be done in a
    * {@link jakarta.ws.rs.container.ContainerRequestFilter}.
    */
   String DEFAULT_CHARSET_PROPERTY =
    "resteasy.provider.multipart.inputpart.defaultCharset";

   /**
    * @return headers of this part
    */
   MultivaluedMap<String, String> getHeaders();
   String getBodyAsString() throws IOException;
   <T> T getBody(Class<T> type, Type genericType) throws IOException;
   <T> T getBody(GenericType<T> type) throws IOException;

   /**
    * @return "Content-Type" of this part
    */
   MediaType getMediaType();

   /**
    * @return true if the Content-Type was resolved from the message, false if
    *         it was resolved from the server default
    */
   boolean isContentTypeFromMessage();

   /**
    * Change the media type of the body part before you extract it.
    * Useful for specifying a charset.
    * @param mediaType media type
    */
   void setMediaType(MediaType mediaType);
}
----

[[_simple_mulitpart_message_example]]
==== Simple multipart/mixed message example

The following example shows how to read and write a simple multipart/mixed message. 

The data to be transferred is a very simple class, Soup.

[source,java]
----
package org.jboss.resteasy.test.providers.multipart.resource;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;

@XmlRootElement(name = "soup")
@XmlAccessorType(XmlAccessType.FIELD)
public class Soup {
    @XmlElement
    private String id;

    public Soup(){}
    public Soup(final String id){this.id = id;}
    public String getId(){return id;}
}
----

This code fragment creates a multipart/mixed message passing Soup information using class, `MultipartOutput`. 

[source,java]
----
MultipartOutput multipartOutput = new MultipartOutput();
multipartOutput.addPart(new Soup("Chicken Noodle"),
    MediaType.APPLICATION_XML_TYPE);
multipartOutput.addPart(new Soup("Vegetable"),
    MediaType.APPLICATION_XML_TYPE);
multipartOutput.addPart("Granny's Soups", MediaType.TEXT_PLAIN_TYPE);
----

This code fragment uses class `MultipartInput` to extract the Soup information provided by multipartOutput above. 

[source,java]
----
public void process() {
    // MultipartInput, the entity returned in the client in a
    // Response object or the input value of an endpoint method parameter.
    for (InputPart inputPart : multipartInput.getParts()) {
        if (MediaType.APPLICATION_XML_TYPE.equals(inputPart.getMediaType())) {
          Soup c = inputPart.getBody(Soup.class, null);
          String name = c.getId();
        } else {
          String s = inputPart.getBody(String.class, null);
        }
    }
}
----

Returning a multipart/mixed message from an endpoint can be done in two ways. `MultipartOutput` can be returned as the method's return object or as an entity in a `Response` object. 

[source,java]
----
@GET
@Path("soups/obj")
@Produces("multipart/mixed")
public MultipartOutput soupsObj() {
    return multipartOutput;
}

@GET
@Path("soups/resp")
@Produces("multipart/mixed")
public Response soupsResp() {
  return Response.ok(multipartOutput, MediaType.valueOf("multipart/mixed"))
                 .build();
}
----

There is no difference in the way a client retrieves the message from the endpoint.
It is done as follows. 

[source,java]
----
public void process() {
    try (Client client = ClientBuilder.newClient()) {
        WebTarget target = client.target(THE_URL);
        try (Response response = target.request().get()) {
            MultipartInput multipartInput = response.readEntity(MultipartInput.class);
            for (InputPart inputPart : multipartInput.getParts()) {
                if (MediaType.APPLICATION_XML_TYPE.equals(inputPart.getMediaType())) {
                  Soup c = inputPart.getBody(Soup.class, null);
                  String name = c.getId();
                } else {
                  String s = inputPart.getBody(String.class, null);
                }
            }
        }
    }
}
----

A client sends the message, multipartOutput, to an endpoint as an entity object in an HTTP method call in this code fragment. 

[source,java]
----
Client client = ClientBuilder.newClient();
WebTarget target = client.target(SOME_URL + "/register/soups");
Entity<MultipartOutput> entity = Entity.entity(multipartOutput,
        new MediaType("multipart", "mixed"));
Response response = target.request().post(entity);
----

Here is the endpoint receiving the message and extracting the contents. 

[source,java]
----
 @POST
 @Consumes("multipart/mixed")
 @Path("register/soups")
  public void registerSoups(MultipartInput multipartInput) throws IOException {

      for (InputPart inputPart : multipartInput.getParts()) {
         if (MediaType.APPLICATION_XML_TYPE.equals(inputPart.getMediaType())) {
                Soup c = inputPart.getBody(Soup.class, null);
                String name = c.getId();
         } else {
                String s = inputPart.getBody(String.class, null);
         }
      }
  }
----

[[_message_with_generictype_example]]
==== Multipart/mixed message with GenericType example

This example shows how to read and write a multipart/mixed message whose content consists of a generic type, in this case a List<Soup>. The `MultipartOutput` and `MultipartIntput` methods that use `GenericType` parameters are used. 

The multipart/mixed message is created using `MultipartOutput` as follows. 

[source,java]
----
public void process() {
    MultipartOutput multipartOutput = new MultipartOutput();
    List<Soup> soupList = new ArrayList<Soup>();
    soupList.add(new Soup("Chicken Noodle"));
    soupList.add(new Soup("Vegetable"));
    multipartOutput.addPart(soupList, new GenericType<List<Soup>>(){},
           MediaType.APPLICATION_XML_TYPE );
    multipartOutput.addPart("Granny's Soups", MediaType.TEXT_PLAIN_TYPE);
}
----

The message data is extracted with `MultipartInput`.
Note there are two `MultipartInput` getBody methods that can be used to retrieve data specifying `GenericType`.
This code fragment uses the second one but shows the first one in comments. 

[source,java]
----
<T> T getBody(Class<T> type, Type genericType) throws IOException;
<T> T getBody(GenericType<T> type) throws IOException;
----

[source,java]
----
public void process() {
            
   // MultipartInput multipartInput, the entity returned in the client in a
   // Response object or the input value of an endpoint method parameter.
   GenericType<List<Soup>> gType = new GenericType<List<Soup>>(){};

   for (InputPart inputPart : multipartInput.getParts()) {
      if (MediaType.APPLICATION_XML_TYPE.equals(inputPart.getMediaType())) {
         List<Soup> c = inputPart.getBody(gType);
      } else {
         String s = inputPart.getBody(String.class, null);;
      }
   }
}
----

[[_multipart_list_example]]
==== java.util.List with multipart/mixed data example

When a set of message parts are uniform they do not need to be written using `MultipartOutput` or read with `MultipartInput`.
They can be sent and received as a `List`.
RESTEasy performs the necessary work to read and write the message data. 

For this example the data to be transmitted is class, `ContextProvidersCustomer`

[source,java]
----
package org.jboss.resteasy.test.providers.multipart.resource;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class ContextProvidersCustomer {
   @XmlElement
   private String name;

   public ContextProvidersCustomer() { }
   public ContextProvidersCustomer(final String name) {
      this.name = name;
  }
   public String getName() { return name;}
}
----

In this code fragment the client creates and sends a list of `ContextProvidersCustomers`. 

[source,java]
----
List<ContextProvidersCustomer> customers =
    new ArrayList<ContextProvidersCustomer>();
customers.add(new ContextProvidersCustomer("Bill"));
customers.add(new ContextProvidersCustomer("Bob"));

Entity<ContextProvidersCustomer> entity = Entity.entity(customers,
new MediaType("multipart", "mixed"));

Client client = ClientBuilder.newClient();
WebTarget target = client.target(SOME_URL);
Response response = target.request().post(entity);
----

The endpoint receives the list, alters the contents and returns a new list. 

[source,java]
----
@POST
@Consumes("multipart/mixed")
@Produces(MediaType.APPLICATION_XML)
@Path("post/list")
public List<ContextProvidersName> postList(
     List<ContextProvidersCustomer> customers) throws IOException {

  List<ContextProvidersName> names = new ArrayList<ContextProvidersName>();

  for (ContextProvidersCustomer customer : customers) {
     names.add(new ContextProvidersName("Hello " + customer.getName()));
  }
  return names;
}
----

The client receives the altered message data and processes it. 

[source,java]
----
Response response = target.request().post(entity);
List<ContextProvidersCustomer> rtnList =
  response.readEntity(new GenericType<List<ContextProvidersCustomer>>(){});
----

[[_multipart_related_group]]
=== Multipart/related

The Multipart/Related mime type is intended for compound objects consisting of several inter-related body parts, (RFC2387).  There is a root or start part.
All other parts are referenced from the root part.
Each part has a unique id.
The type and the id of the start part is presented in parameters in the message content-type header. 

[[_writing_multipart_related]]
==== Writing multipart/related messages

RESTEasy provides class `MultipartRelatedOutput` to assist the user in specifying the required information and generating a properly formatted message. `MultipartRelatedOutput` is a subclass of `MultipartOutput`. 

[source,java]
----

         
package org.jboss.resteasy.plugins.providers.multipart;

import jakarta.ws.rs.core.MediaType;

public class MultipartRelatedOutput extends MultipartOutput {
   private String startInfo;

   /**
    * The part used as the root.
    */
   public OutputPart getRootPart();

   /**
    * entity  object representing the part's body
    * mediaType Content-Type of the part
    * contentId  Content-ID to be used as identification for the current
    *            part, optional, if null one will be generated
    * contentTransferEncoding
    *            value used for the Content-Transfer-Encoding header
    *            field of the part. It's optional, if you don't want to set
    *            this pass null. Example values are: "7bit",
    *            "quoted-printable", "base64", "8bit", "binary"
    */
   public OutputPart addPart(Object entity, MediaType mediaType,
         String contentId, String contentTransferEncoding);

   /**
    * start-info parameter of the Content-Type.  An optional parameter.
    * As described in RFC2387, section 3.3. The Start-Info Parameter
    */
   public String getStartInfo();
}
----

[[_reading_multipart_related]]
==== Reading multipart/related messages

`MultipartRelatedInput` is an interface class that provides access to multipart/related message data.
It is a subclass of `MultipartInput`.
RESTEasy provides an implementation of this class.
It performs the work to retrieve message data. 

[source,java]
----
package org.jboss.resteasy.plugins.providers.multipart;

import jakarta.ws.rs.core.MediaType;

public class MultipartRelatedOutput extends MultipartOutput {
   private String startInfo;

   /**
    * The part used as the root.
    */
   public OutputPart getRootPart();

   /**
    * entity  object representing the part's body
    * mediaType Content-Type of the part
    * contentId  Content-ID to be used as identification for the current
    *            part, optional, if null one will be generated
    * contentTransferEncoding
    *            value used for the Content-Transfer-Encoding header
    *            field of the part. It's optional, if you don't want to set
    *            this pass null. Example values are: "7bit",
    *            "quoted-printable", "base64", "8bit", "binary"
    */
   public OutputPart addPart(Object entity, MediaType mediaType,
         String contentId, String contentTransferEncoding);

   /**
    * start-info parameter of the Content-Type.  An optional parameter.
    * As described in RFC2387, section 3.3. The Start-Info Parameter
    */
   public String getStartInfo();
}
----

[[_multipart_related_example]]
==== Multipart/related message example

The client in this example creates a multipart/related message, POSTs it to the endpoint and processes the multipart/related message returned by the endpoint. 

[source,java]
----
public void process() {
    MultipartRelatedOutput mRelatedOutput = new MultipartRelatedOutput();
    mRelatedOutput.setStartInfo("text/html");
    mRelatedOutput.addPart("Bill", new MediaType("image", "png"), "bill", "binary");
    mRelatedOutput.addPart("Bob", new MediaType("image", "png"), "bob", "binary");

    Entity<MultipartRelatedOutput> entity = Entity.entity(mRelatedOutput,
        new MediaType("multipart", "related"));

    try (Client client = ClientBuilder.newClient()) {
        WebTarget target = client.target(SOME_URL);
        try (Response response = target.request().post(entity)) {
            MultipartRelatedInput result = response.readEntity(
                  MultipartRelatedInput.class);
            Map<String, InputPart> map = result.getRelatedMap();
            Set<String> keys = map.keySet();
            boolean a = keys.contains("Bill");
            boolean b = keys.contains("Bob");
            for (InputPart inputPart : map.values()) {
                String alterName = inputPart.getBody(String.class, null);
            }
        }
    }
}
----

Here is the endpoint the client above is calling. 

[source,java]
----
@POST
@Consumes("multipart/related")
@Produces("multipart/related")
@Path("post/related")
public MultipartRelatedOutput postRelated(MultipartRelatedInput input)
        throws IOException {

  MultipartRelatedOutput rtnMRelatedOutput = new MultipartRelatedOutput();
        rtnMRelatedOutput.setStartInfo("text/html");

  for (Iterator<InputPart> it = input.getParts().iterator(); it.hasNext(); ) {
      InputPart part = it.next();
      String name = part.getBody(String.class, null);
      rtnMRelatedOutput.addPart("Hello " + name,
                    new MediaType("image", "png"), name, null);
  }
  return rtnMRelatedOutput;
}
----

[[_xml_binary_optimized_packaging]]
==== XML-binary Optimized Packaging (XOP)

RESTEasy supports XOP messages packaged as multipart/related messages (http://www.w3.org/TR/xop10/). A {xml-binding} annotated POJO that also holds binary content can be transmitted using XOP.
XOP allows the binary data to skip going through the XML serializer because binary data can be serialized differently from text and this can result in faster transport time. 

RESTEasy requires annotation `@XopWithMultipartRelated` to be placed on any endpoint method that returns an object that is to be processed with XOP and on any endpoint input parameter that is to be processed by XOP.

RESTEasy highly recommends, if you know the exact mime type of the POJO's binary data, tag the field with annotation @XmlMimeType.
This annotation tells {xml-binding} the mime type of the binary content, however this is not required in order to do XOP packaging.

[[_xopwithmultipartrelated_return_object_example]]
==== @XopWithMultipartRelated return object example

The data to be transmitted is class, `ContextProvidersXop`.
Note that field, `bytes`, is identified as an application/octet-stream mime type using annotation @XmlMimeType 

[source,java]
----
package org.jboss.resteasy.test.providers.multipart.resource;

import jakarta.ws.rs.core.MediaType;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlMimeType;
import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ContextProvidersXop {

   @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM)
   private byte[] bytes;

   public ContextProvidersXop(final byte[] bytes) {
      this.bytes = bytes;
   }

   public ContextProvidersXop() {}
   public byte[] getBytes() {return bytes;}
   public void setBytes(byte[] bytes) {this.bytes = bytes;}
}
----

The endpoint returns an instance of `ContextProvidersXop`.
Note annotation `@XopWithMultipartRelated` is declared on the method because we want the return object to use XOP packaging.

[source,java]
----
@GET
@Path("get/xop")
@Produces("multipart/related")
@XopWithMultipartRelated
public ContextProvidersXop getXop() {
    return new ContextProvidersXop("goodbye world".getBytes());
}
----

The client retrieves the data as follows

[source,java]
----
public void process() {
    try (Client client = ClientBuilder.newClient()) {
        WebTarget target = client.target(SOME_URL);
        try (Response response = target.request().get()) {
            ContextProvidersXo entity = response.readEntity(ContextProvidersXop.class);
        }
    }
}
----

[[_xopwithmultipartrelated_input_parameter_example]]
==== @XopWithMultipartRelated input parameter example

Here is an endpoint that has an input parameter that is transmitted as an XOP package.
Note the @XopWithMultipartRelated annotation on input parameter xop. 

[source,java]
----
@POST
@Path("post/xop")
@Consumes("multipart/related")
public String postXop(@XopWithMultipartRelated ContextProvidersXop xop) {
    return new String(xop.getBytes());
}
----

This client is sending the data to the endpoint above.

[source,java]
----
ContextProvidersXop xop = new ContextProvidersXop("hello world".getBytes());
Entity<ContextProvidersXop> entity = Entity.entity(xop,
    new MediaType("multipart", "related"));

Client client = ClientBuilder.newClient();
WebTarget target = client.target(SOME_URL);
Response response = target.request().post(entity);
----

[[_multipart_formdata_group]]
=== Multipart/form-data

The MultiPart/Form-Data mime type is used in sending form data (rfc2388). It can include data generated by user input, information that is typed, or included from files that the user has selected.
"multipart/form-data" is often found in web application HTML Form documents and is generally used to upload files.
The form-data format is the same as other multi-part formats, except that each inlined piece of content has a name associated with it. 

[[_entity_part]]
==== Multipart/form-data with EntityParts

In {spec-name} 3.1 the `jakarta.ws.rs.core.EntityPart` API was introduced.
This can be used to read and write `multipart/form-data` with a standardized API. 

The content of the `EntityPart` has two definable size limits. It also allows you to override the temporary directory
where files will be written to. These can be set as system properties, servlet context properties or a MicroProfile
Config compatible value.

[cols="1,1,1,1", options="header"]
|===
| Property Name
| Description
| Default Value
| Example

| `dev.resteasy.entity.memory.threshold`
| The threshold to use for the amount of data to store in memory for entities.
| 5MB
| 512MB is equivalent to 512 Megabytes

| `dev.resteasy.entity.file.threshold`
| The threshold to use for the amount of data that can be stored in a file for entities. If the threshold is reached an
  `IllegalStateException` will be thrown. A value of -1 means no limit.
|50MB
|1GB is equivalent to 1 Gigabyte

| `dev.resteasy.entity.tmpdir`
| The temporary directory to use when the `dev.resteasy.entity.memory.threshold` was reached and the entity must be
  written to a file. Note that the directory must already exist.
`IllegalStateException` will be thrown. A value of -1 means no limit.
| The value of the `java.io.tmpdir` system property.
| `/tmp/entity`
|===

You can read, write and inject (with `@FormParam`) an `EntityPart` in the following forms; 

* `EntityPart`
* `List<EntityPart>`
* `InputStream`

Example reading data: 
[source,java]
----
public void process() {
    try (Client client = ClientBuilder.newClient()) {
        final ListEntityPart multipart = new ArrayList();
        multipart.add(
                EntityPart.withName("content")
                        .content("Example Content")
                        .mediaType(MediaType.TEXT_PLAIN_TYPE)
                        .build()
        );
        try (
                Response response = client.target(INSTANCE.configuration().baseUriBuilder().path("test/injected"))
                        .request(MediaType.MULTIPART_FORM_DATA_TYPE)
                        .post(Entity.entity(new GenericEntity(multipart) {
                        }, MediaType.MULTIPART_FORM_DATA))
        ) {
            Assert.assertEquals(Response.Status.OK, response.getStatusInfo());
            final ListEntityPart entityParts = response.readEntity(new GenericType() {
            });
        }
    }
}
----

Example receiving and writing data.

[source,java]
----
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.MULTIPART_FORM_DATA)
@Path("/injected")
public ListEntityPart injected(@FormParam("content") final String string,
                         @FormParam("content") final EntityPart entityPart,
                         @FormParam("content") final InputStream in) throws IOException {
    final ListEntityPart multipart = new ArrayList();
    multipart.add(
            EntityPart.withName("received-entity-part")
                    .content(entityPart.getContent(String.class))
                    .mediaType(entityPart.getMediaType())
                    .fileName(entityPart.getFileName().orElse(null))
                    .build()
    );
    multipart.add(
            EntityPart.withName("received-input-stream")
                    .content(MultipartEntityPartProviderTest.toString(in).getBytes(StandardCharsets.UTF_8))
                    .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
                    .build()
    );
    multipart.add(
            EntityPart.withName("received-string")
                    .content(string)
                    .mediaType(MediaType.TEXT_PLAIN_TYPE)
                    .build()
    );
    return multipart;
}
----

[[_writing_multipart_formdata]]
==== Writing multipart/form-data messages

Form data consists of key/value pairs.
RESTEasy provides class `MultipartFormDataOutput` to assist the user in specifying the required information and generating a properly formatted message.
It is a subclass of `MultipartOutput`.
And as with multipart/mixed data sometimes there may be marshalling which is sensitive to generic type metadata, in those cases use the methods containing input parameter GenericType. 

[source,java]
----
package org.jboss.resteasy.plugins.providers.multipart;

public class MultipartFormDataOutput extends MultipartOutput {
    public OutputPart addFormData(String key, Object entity,
            MediaType mediaType)
    public OutputPart addFormData(String key, Object entity, GenericType type,
            MediaType mediaType)
    public OutputPart addFormData(String key, Object entity, Class type,
            Type genericType, MediaType mediaType)
    public Map<String, OutputPart> getFormData()
    public Map<String, List<OutputPart>> getFormDataMap()
}
----

[[_reading_multipart_formdata]]
==== Reading multipart/form-data messages

`MultipartFormDataInput` is an interface class that provides access to multipart/form-data message data.
It is a subclass of `MultipartInput`.
RESTEasy provides an implementation of this class.
It performs the work to retrieve message data. 

[source,java]
----
package org.jboss.resteasy.plugins.providers.multipart;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import jakarta.ws.rs.core.GenericType;

public interface MultipartFormDataInput extends MultipartInput {
   /**
    * @return A parameter map containing a list of values per name.
    */
   Map<String, List<InputPart>> getFormDataMap();
   <T> T getFormDataPart(String key, Class<T> rawType, Type genericType)
         throws IOException;
   <T> T getFormDataPart(String key, GenericType<T> type) throws IOException;
}
----

[[_simple_multipart_formdata_example]]
==== Simple multipart/form-data message example

The following example show how to read and write a simple multipart/form-data message. 

The multipart/mixed message is created on the clientside using the `MultipartFormDataOutput` object.
One piece of form data to be transfered is a very simple class, `ContextProvidersName`. 

[source,java]
----
package org.jboss.resteasy.test.providers.multipart.resource;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "name")
@XmlAccessorType(XmlAccessType.FIELD)
public class ContextProvidersName {
   @XmlElement
   private String name;

   public ContextProvidersName() {}
   public ContextProvidersName(final String name) {this.name = name;}
   public String getName() {return name;}
}
----

The client creates and sends the message as follows: 

[source,java]
----
public void process() {
    MultipartFormDataOutput output = new MultipartFormDataOutput();
    output.addFormData("bill", new ContextProvidersCustomer("Bill"),
     MediaType.APPLICATION_XML_TYPE);
    output.addFormData("bob", "Bob", MediaType.TEXT_PLAIN_TYPE);

    Entity<MultipartFormDataOutput> entity = Entity.entity(output,
      new MediaType("multipart", "related"));

    try (Client client = ClientBuilder.newClient()) {
        WebTarget target = client.target(SOME_URL);
        try (Response response = target.request().post(entity)) {
        }
    }
}
----

The endpoint receives the message and processes it. 

[source,java]
----
@POST
@Consumes("multipart/form-data")
@Produces(MediaType.APPLICATION_XML)
@Path("post/form")
public Response postForm(MultipartFormDataInput input)
     throws IOException {

  Map<String, List<InputPart>> map = input.getFormDataMap();
  List<ContextProvidersName> names = new ArrayList<ContextProvidersName>();

  for (Iterator<String> it = map.keySet().iterator(); it.hasNext(); ) {
     String key = it.next();
     InputPart inputPart = map.get(key).iterator().next();
     if (MediaType.APPLICATION_XML_TYPE.equals(inputPart.getMediaType())) {
        names.add(new ContextProvidersName(inputPart.getBody(
              ContextProvidersCustomer.class, null).getName()));
     } else {
        names.add(new ContextProvidersName(inputPart.getBody(
              String.class, null)));
     }
  }
  return Response.ok().build();
}
----

[[_map_with_multipart_formdata]]
==== java.util.Map with multipart/form-data

When the data of a multipart/form-data message is uniform it does not need to be written in a `MultipartFormDataOutput` object.
It can be sent and received as a `java.util.Map` object.
RESTEasy performs the necessary work to read and write the message data, however the Map object must declare the type it is unmarshalling via the generic parameters in the Map type declaration. 

Here is an example of a client creating and sending a multipart/form-data message. 

[source,java]
----
public void process() {
    Map<String, ContextProvidersCustomer> customers =
          new HashMap<String, ContextProvidersCustomer>();
    customers.put("bill", new ContextProvidersCustomer("Bill"));
    customers.put("bob", new ContextProvidersCustomer("Bob"));

    Entity<Map<String, ContextProvidersCustomer>> entity =
    Entity.entity(customers, new MediaType("multipart", "form-data"));

    try (Client client = ClientBuilder.newClient()) {
        WebTarget target = client.target(SOME_URL);
        try (Response response = target.request().post(entity)) {
        }
    }
}
----

This is the endpoint the client above is calling.
It receives the message and processes it. 

[source,java]
----
 @POST
 @Consumes("multipart/form-data")
 @Produces(MediaType.APPLICATION_XML)
 @Path("post/map")
 public Response postMap(Map<String, ContextProvidersCustomer> customers)
         throws IOException {

   List<ContextProvidersName> names = new ArrayList<ContextProvidersName>();
   for (Iterator<String> it = customers.keySet().iterator(); it.hasNext(); ) {
       String key = it.next();
       ContextProvidersCustomer customer = customers.get(key);
       names.add(new ContextProvidersName(key + ":" + customer.getName()));
   }
   return Response.ok().build();
 }
----

[[_multipart_formdata_output]]
==== Multipart/form-data java.util.Map as method return type

A `java.util.Map` object representing a multipart/form-data message can be returned from an endpoint as long as the message data is uniform,  however the endpoint method MUST be annotated with @PartType which declares the media type of the Map entries and the Map object must declare the type it is unmarshalling via the generic parameters in the Map type declaration.
RESTEasy requires this information so it can generate the message properly.

Here is an example of an endpoint returning a Map of `ContextProvidersCustomer` to the client. 

[source,java]
----
@GET
@Produces("multipart/form-data")
@PartType("application/xml")
@Path("get/map")
public Map<String, ContextProvidersCustomer> getMap() {

  Map<String, ContextProvidersCustomer> map =
      new HashMap<String, ContextProvidersCustomer>();
  map.put("bill", new ContextProvidersCustomer("Bill"));
  map.put("bob", new ContextProvidersCustomer("Bob"));
  return map;
}
----

The client would retrieve the data as follows.

[source,java]
----
public void process() {
    try (Client client = ClientBuilder.newClient()) {
        WebTarget target = client.target(SOME_URL);
        try (Response response = target.request().get()) {
            MultipartFormDataInput entity = response.readEntity(MultipartFormDataInput.class);
        }
    }

    ContextProvidersCustomer bill = entity.getFormDataPart("bill",
        ContextProvidersCustomer.class, null);
    ContextProvidersCustomer bob = entity.getFormDataPart("bob",
        ContextProvidersCustomer.class, null);
}
----

[[_multipartform_annotation]]
==== @MultipartForm and POJOs

If you have exact knowledge of the multipart/form-data packets, they can be mapped to and from a POJO class using the annotation `@org.jboss.resteasy.annotations.providers.multipart.MultipartForm` and the {spec-name} `@FormParam` annotation.
Simply define a POJO with at least a default constructor and annotate its fields and/or properties with `@FormParams`.
These `@FormParams` must also be annotated with `@org.jboss.resteasy.annotations.providers.multipart.PartType` if doing output.
For example: 

[source,java]
----
public class CustomerProblemForm {
    @FormParam("customer")
    @PartType("application/xml")
    private Customer customer;

    @FormParam("problem")
    @PartType("text/plain")
    private String problem;

    public Customer getCustomer() { return customer; }
    public void setCustomer(Customer cust) { this.customer = cust; }
    public String getProblem() { return problem; }
    public void setProblem(String problem) { this.problem = problem; }
}
----

After defining the POJO class it can be used to represent multipart/form-data.
Here's an example of sending a `CustomerProblemForm` using the RESTEasy client framework: 

[source,java]
----
@Path("portal")
public class CustomerPortal {

   @Path("issues/{id}")
   @Consumes("multipart/form-data")
   @PUT
   public void putProblem(@MultipartForm CustomerProblemForm form,
                          @PathParam("id") int id) {
      CustomerPortal portal = ProxyFactory.create(
            CustomerPortal.class, "http://example.com");
      portal.putProblem(form, 333);
   }
}
----

Note that the `@MultipartForm` annotation was used to tell RESTEasy that the object has a `@FormParam` and that it should be marshalled from that.
The same object can be used to receive multipart data.
Here is an example of the server side counterpart of our customer portal. 

[source,java]
----
@Path("portal")
public class CustomerPortalServer {

    @Path("issues/{id}")
    @Consumes("multipart/form-data")
    @PUT
    public void putIssue(@MultipartForm CustoemrProblemForm form,
                         @PathParam("id") int id) {
    }
}
----

In addition to the XML data format, JSON formatted data can be used to represent POJO classes.
To achieve this, plug in a JSON provider into your project.
For example, add the RESTEasy Jackson2 Provider into the project's dependency scope: 

[source,xml,subs="attributes+"]
----
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson2-provider</artifactId>
    <version>{resteasy-version}</version>
</dependency>
----

Now an ordinary POJO class can be written, which Jackson2 will automatically serialize/deserialize into JSON format: 

[source,java]
----
public class JsonUser {
   private String name;

   public JsonUser() {}
   public JsonUser(final String name) { this.name = name; }
   public String getName() { return name; }
   public void setName(String name) { this.name = name; }
}
----

The resource class can be written like this:

[source,java]
----
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import org.jboss.resteasy.annotations.providers.multipart.PartType;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;

@Path("/")
public class JsonFormResource {

    public JsonFormResource() {
    }

    public static class Form {

    @FormParam("user")
    @PartType("application/json")
    private JsonUser user;

    public Form() {
    }

    public Form(final JsonUser user) {
    this.user = user;
    }

    public JsonUser getUser() {
      return user;
    }
}

    @PUT
    @Path("form/class")
    @Consumes("multipart/form-data")
    public String putMultipartForm(@MultipartForm Form form) {
         return form.getUser().getName();
    }
}
----

In the code shown above, it can be seen the PartType of JsonUser is marked as "application/json", and it's included in the "@MultipartForm Form" class instance. 

To send the request to the resource method, send the JSON formatted data that corresponds with the JsonUser class.
The easiest way to do this is to use a proxy class that has the same definition of the resource class.
Here is the sample code of the proxy class that is corresponding with the JsonFormResource class: 

[source,java]
----
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;

@Path("/")
public interface JsonForm {

    @PUT
    @Path("form/class")
    @Consumes("multipart/form-data")
    String putMultipartForm(@MultipartForm JsonFormResource.Form form);
}
----

And then use the proxy class above to send the request to the resource method correctly.
Here is the sample code: 

[source,java]
----
ResteasyClient client = (ResteasyClient)ClientBuilder.newClient();
JsonForm proxy = client.target("your_request_url_address")
                       .proxy(JsonForm.class);
String name = proxy.putMultipartForm(new JsonFormResource
                   .Form(new JsonUser("bill")));
----

If the client side has the Jackson2 provider included, the request will be marshaled correctly.
The JsonUser data will be converted into JSON format and sent to the server side.
Hand-crafted JSON data can be use in the request and sent to the server side.
It must be made sure the request data is in the correct form. 

[[_multipart_parsing_note]]
=== Note about multipart parsing and working with other frameworks

There are many frameworks doing multipart parsing automatically with the help of filters and interceptors, like `org.jboss.seam.web.MultipartFilter` in Seam and `org.springframework.web.multipart.MultipartResolver` in Spring, however these incoming multipart request stream can be parsed only once.
RESTEasy users working with multipart should make sure that nothing parses the stream before RESTEasy gets it. 

[[_multipart_overwrite_default_content_type]]
=== Overwriting the default fallback content type for multipart messages

By default if no Content-Type header is present in a part `"text/plain; charset=us-ascii"` is used as the fallback.
This is the value defined by the MIME RFC, however some web clients, and web browsers do not send Content-Type headers for all fields in a multipart/form-data request.
They send them only for the file parts.
This can cause character encoding and unmarshalling errors on the server side.
To correct this there is an option to define another, non-rfc compliant fallback value.
This can be done dynamically per request with the filter facility of {spec-name} 3.0.
In the following example we will set `"\*/*; charset=UTF-8"` as the new default fallback:

[source,java]
----
import org.jboss.resteasy.plugins.providers.multipart.InputPart;

@Provider
public class InputPartDefaultCharsetOverwriteContentTypeCharsetUTF8
   implements ContainerRequestFilter {

   @Override
   public void filter(ContainerRequestContext requestContext) throws IOException {
      requestContext.setProperty(InputPart.DEFAULT_CONTENT_TYPE_PROPERTY, "*/*; charset=UTF-8");
   }
}
----

[[_multipart_overwrite_content_type]]
=== Overwriting the content type for multipart messages

Using attribute, InputPart.DEFAULT_CONTENT_TYPE_PROPERTY and a filter enables the setting of a default Content-Type, It is also possible to override the Content-Type by setting a different media type with method `InputPart.setMediaType()`.
Here is an example: 

[source,java]
----
@POST
@Path("query")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public Response setMediaType(MultipartInput input) throws IOException {
    List<InputPart> parts = input.getParts();
    InputPart part = parts.get(0);
    part.setMediaType(MediaType.valueOf("application/foo+xml"));
    String s = part.getBody(String.class, null);
}
----

[[_multipart_overwrite_default_charset]]
=== Overwriting the default fallback charset for multipart messages

Sometimes, a part may have a Content-Type header with no charset parameter.
If the `InputPart.DEFAULT_CONTENT_TYPE_PROPERTY` property is set and the value has a charset parameter, that value will be appended to an existing Content-Type header that has no charset parameter.
It is also possible to specify a default charset using the constant `InputPart.DEFAULT_CHARSET_PROPERTY` (actual value "resteasy.provider.multipart.inputpart.defaultCharset"): 

[source,java]
----
import org.jboss.resteasy.plugins.providers.multipart.InputPart;

@Provider
public class InputPartDefaultCharsetOverwriteContentTypeCharsetUTF8
   implements ContainerRequestFilter {

   @Override
   public void filter(ContainerRequestContext requestContext) throws IOException {
      requestContext.setProperty(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8");
   }
}
----

If both `InputPart.DEFAULT_CONTENT_TYPE_PROPERTY` and `InputPart.DEFAULT_CHARSET_PROPERTY` are set, then the value of
`InputPart.DEFAULT_CHARSET_PROPERTY` will override any charset in the value of `InputPart.DEFAULT_CONTENT_TYPE_PROPERTY`.