Multipart Uploads

Note: introduced in 0.3.0.

The Multipart Plugin provides a friendlier API to upload files using the multipart/form-data) Content Type. This used to be core functionality, but was dropped as of 0.3.0 due to the need of an extra dependency and multipart uploads not being common use.

Attention! In order to use this feature, you have to install the http-form_data gem!

How to Use

File submission

If you want to upload images, your server will more than probably expect them to be sent using the multipart/form-data encoding. You can do that by passing HTTP::FormData objects to the :form parameter:

HTTPX.plugin(:multipart).post("https://example.com", form: {image: HTTP::FormData::File.new(path)})

# 
# ...
# Content-Type: multipart/form-data; boundary=---------------------a324a748aade297ac4ffc87108bee9ecd8cb2c2fea ...
# "-----------------------2a6965a264bbf67e519ad96cbc970800cac79dd240
# ...
# Content-Disposition: form-data; name="image"; filename="image.jpg"
# Content-Type: application/octet-stream
#
# \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xFF\xDB\x00\x84\x00\x02\x02\x02

Content Types

You can also post more complex multipart, with different content types. For instance, if you upload a video:

HTTPX.plugin(:multipart).post("https://example.com", form: {video: HTTP::FormData::File.new(video_path, content_type: "video/mp4")})

# 
# ...
# Content-Type: multipart/form-data; boundary=---------------------a324a748aade297ac4ffc87108bee9ecd8cb2c2fea ...
# "-----------------------2a6965a264bbf67e519ad96cbc970800cac79dd240
# ...
# Content-Disposition: form-data; name="video"; filename="video.mp4"
# Content-Type: video/mp4
#
# \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xFF\xDB\x00\x84\x00\x02\x02\x02

Or you have to combine different parameters with different encodings:

HTTPX.plugin(:multipart).post("https://example.com", form: {
  video: HTTP::FormData::File.new(video_path, content_type: "video/mp4"),
  metadata: HTTP::FormData::Part.new(JSON.dump({foo: bar}), content_type: "application/json"),
  ping: "pong"
})

# 
# ...
# Content-Type: multipart/form-data; boundary=---------------------a324a748aade297ac4ffc87108bee9ecd8cb2c2fea ...
# "-----------------------2a6965a264bbf67e519ad96cbc970800cac79dd240
# ...
# Content-Disposition: form-data; name="video"; filename="video.mp4"
# Content-Type: video/mp4
#
# \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xFF\xDB\x00\x84\x00\x02\x02\x02.....
# Content-Disposition: form-data; name="metadata"
# Content-Type: application/json
#
# {"foo":"bar"}
# Content-Disposition: form-data; name="ping"
#
# pong

Things to consider when using the plugin:

  • you should pass the files to upload in the :form field.
  • the parameter name and filename will be inferred from the hash keys when the value is from the HTTP::FormData::Part type;
  • you can pass multiple parts/files as elements of the hash;
  • You should get acquainted with the types defined in http-form_data;
  • By default, HTTP::FormData::Part sets the text/plain content type, and HTTP::FormData::File defaults to application/octet-stream. If you require any other content type, you’ll have to do it yourself.
  • HTTP::FormData types are not rewindable, so make sure you don’t pass the same object to different requests, as the parameters will be sent only once
# bad
params = {
  image: HTTP::FormData::File.new(path)
}
HTTPX.plugin(:multipart).post("https://example.com", form: params)
HTTPX.plugin(:multipart).post("https://example2.com", form: params)

# good
HTTPX.plugin(:multipart).post("https://example.com", form: { image: HTTP::FormData::File.new(path) })
HTTPX.plugin(:multipart).post("https://example2.com", form: { image: HTTP::FormData::File.new(path) })

Next: Persistent