Skip to main content

How can I process multipart HTTP uploads in workflows?

· 4 min read
Jeremy Scott
Co-founder

Version 3.1.0 of opscotch adds support for HTTP multipart uploads, letting you receive text/JSON and binary content in the same request. This unlocks use cases like “send me the metadata and the file in one hit,” without forcing you to pick one or chain requests. With saftey in mind - this is disabled by default.

Workflows can now accept two multipart fields in a single HTTP request:

  • body: string payload, available from context.getBody()
  • stream: binary payload, available from context.getStream()

Heres what the curl example would look like (observe the specific field names and content type declarations):

curl "http://localhost:11000/upload" \
-X POST \
-F 'body={"version": "2.0", "method": "initialize"}' \
-F 'stream=@a-binary-file;type=application/octet-stream'

Constraints to note:

  • Use the field names body and stream when sending multipart requests.
  • context.getBody() always returns a string; JSON.parse(...) to get a JSON entity when you expect JSON.
  • context.getStream() returns a ByteReader for the uploaded file. See the docs: /docs/next/apireference#JavascriptContext-getStream. A deeper dive on ByteReader and streaming is in Byte manipulation in workflows.
  • If you only send one of the parts, that’s fine—use what you receive. If you send neither, there’s nothing to process.
  • The HTTP trigger must set multiPartUploadByteLimit to enable multipart and cap the upload size. Without it, multipart isn’t accepted for that step.

Enabling multipart on your step

To enable multipart uploads for a step http trigger, you must add the multiPartUploadByteLimit property to define the max size (in bytes):

{
"stepId": "my-http-step",
"trigger": {
"http": {
"server": "myServer",
"method": "POST",
"path": "/upload",
"multiPartUploadByteLimit": 1024
}
}
}

If an upload exceeds the limit, it will be rejected; set the value to what you’re comfortable handling.

Straight text/JSON uploads continue to work with Content-Type: application/json via context.getBody(). Straight binary uploads also work with Content-Type: application/octet-stream via context.getStream()—see How can I upload binary files? for more.

Why this matters

Before 3.1.0, there was no way to receive binary data. When this was added, the next problem was you had to choose: send text/JSON or send binary. That meant bolting together two requests (one for the metadata, one for the file) or stuffing everything into base64. Now you can keep things simple and efficient: one call, two parts, clean separation of concerns.

Typical cases:

  • Send configuration plus a binary blob in one request (the opscotch packaging app is a good example of this). See blog about the new packager
  • Attach metadata for a log bundle and stream the actual archive as the binary.
  • Keep workflows slimmer by avoiding extra parsing or double posts.

How to send multipart uploads

Send both JSON/text and a file in one request by naming the multipart parts body and stream:

curl "http://localhost:11000/upload" \
-X POST \
-H "Accept: application/json" \
-F 'body={"version": "2.0", "method": "initialize"}' \
-F 'stream=@a-binary-file;type=application/octet-stream'

You can use any text for body; just remember it arrives as a string. For other text (YAML, CSV, plain notes), it’s still just a string—parse accordingly.

In your workflow JavaScript processor:

let body = context.getBody();      // string; JSON.parse if needed
let stream = context.getStream(); // ByteReader for the uploaded file

// optional pattern:
// const payload = JSON.parse(body);
// process(stream); // see getStream/ByteReader companion post

If you only care about one part (for example, just the binary), you can ignore the other. Nothing breaks—opscotch hands you what was sent.

Single-part uploads still work

Text/JSON only:

curl "http://localhost:11000/upload" \
-X POST \
-H "Content-Type: application/json" \
--data-binary '{"version": "2.0", "method": "initialize"}'

Binary only (handled via context.getStream()):

curl "http://localhost:11000/upload" \
-X POST \
-H "Accept: application/json" \
-H "Content-Type: application/octet-stream" \
--binary-data '@a-binary-file'

If you’re debating which to use: stick with simple JSON when you only need text; switch to multipart when you need both metadata and bytes; use binary-only when you just need the bytes. For more on streaming and binary handling, see the companion posts on getStream(), How can I upload binary files?, and Byte manipulation in workflows.