Using Multipart

Multipart is a widely-supported encoding on the web. It’s most often used for serializing web forms, especially ones that contain rich media like images. It allows for arbitrary data to be encoded in each part thanks to a unique delimiter boundary that is defined separately. This boundary is guaranteed by the client to not appear anywhere in the data.

Multipart is a powerful encoding, however it is rarely used in its base format. Most commonly, multipart/form-data is used. This encoding adds a "name" property to each part of the multipart data. This is required for serializing web forms. For the rest of this guide, assume we are talking about multipart/form-data unless otherwise specified.

!!! tip Multipart integrates with Content like all other encoding methods in Vapor. See Vapor → Content for more information about the Content protocol.

Let’s take a look at how to decode a multipart/form-data-encoded request.

Decode

Most often, you will be decoding multipart/form-data-encoded requests from a web form. Let’s take a look at what one of these requests might look like. After that, we will take a look at what the HTML form for that request would look like.

Request

Here is an example multipart/form-data-encoded request for creating a new user.

  1. POST /users HTTP/1.1
  2. Content-Type: multipart/form-data; boundary=123
  3. --123
  4. Content-Disposition: form-data; name="name"
  5. Vapor
  6. --123
  7. Content-Disposition: form-data; name="age"
  8. 3
  9. --123
  10. Content-Disposition: form-data; name="image"; filename="droplet.png"
  11. <contents of image>
  12. --123--

You can see the multipart data uses a boundary (in this case it is "123") to separate the data. This will usually be a longer string. The client sending a multipart-encoded request must ensure that the boundary it supplies does not appear anywhere in the content it is sending you. That’s what allows this encoding to be used to send things like files.

Form

There are many ways to create a multipart-encoded request, but the most common is an HTML web form. Here is what the HTML form for this request might have looked like.

  1. <form method="POST" action="/users" enctype="multipart/form-data">
  2. <input type="text" name="name">
  3. <input type="text" name="age">
  4. <input type="file" name="image">
  5. </form>

Take note of the enctype attribute on the <form> as well as the file type input. This is what allows us to send files via the web form.

Content

Now let’s take a look at how we would handle this request in Vapor. The first step (as always with Content) is to create a Codable struct that represents the data structure.

  1. import Vapor
  2. struct User: Content {
  3. var name: String
  4. var age: Int
  5. var image: Data
  6. }

!!! tip You can use File instead of Data if you would also like to access the filename.

Now that we have our User struct, let’s decode that request! We can use the ContentContainer to do this easily.

  1. router.post("users") { req -> Future<HTTPStatus> in
  2. return try req.content.decode(User.self).map(to: HTTPStatus.self) { user in
  3. print(user.name) // "Vapor"
  4. print(user.age) // 3
  5. print(user.image) // Raw image data
  6. return .ok
  7. }
  8. }

Now when you post the form to /users, you should see the information printed in the console. Nice work!

Encode

APIs encode multipart data much less often than they decode it. However, encoding is just as easy with Vapor. Using our same User struct from the previous example, here is how we can encode a multipart-encoded response.

  1. router.get("multipart") { req -> User in
  2. let res = req.makeResponse()
  3. let user = User(name: "Vapor", age: 3, image: Data(...))
  4. res.content.encode(user, as: .formData)
  5. return user
  6. }

!!! tip If you set a default MediaType on your Content types, then you can return them directly in the route closure.

Parsing & Serializing

The Multipart package also offers APIs for parsing and serializing multipart/form-data data without using Codable. Check out the API Docs for more information on using those APIs.