Why it matters
A worker runs your piece code inside a sandbox with a bounded memory budget (about 1 GB, minus overhead — see Limits). Reading a large file into aBuffer holds the entire file in that budget at once, so a big enough file exhausts the
memory and the worker is OOM-killed.
Streaming avoids this: the bytes flow through as a Node Readable, roughly 5 MB at a
time, so no process ever holds the whole file. The transfer is transparent — there are no
extra “chunk” steps in your flow; a streaming action looks and behaves like any other.
When to use it
Which approach should you use?
- Buffer (a
Buffer, the default) — small files of known size where holding the whole file in memory is cheap and simple. - Stream (a
Readable) — large files, files of unknown size, or app-to-app transfers (e.g. downloading a large object from one service and uploading it to another) where buffering would risk exhausting memory.
Pieces that support streaming
Streaming is enabled per action. Today:
More pieces are being enabled over time. Actions not listed here still work — they buffer
the file in memory, which is fine within the size limit.
Building streaming actions
If you are building a piece, you can stream on both sides: read an input file as a stream, and write an output file as a stream.Writing a file as a stream
ctx.files.write accepts a Buffer or a Readable. Pass a Readable — such as an S3
object body or a streaming HTTP response — and it streams straight to storage instead of
being buffered. It returns a file reference string you return from the action, exactly like
the buffered form (see Files).
Reading a file as a stream
Addstreaming: true to a Property.File. The property then resolves to an
ApStreamingFile instead of an ApFile:
body directly. Prefer an uploader that accepts a stream of unknown length — for S3
that is Upload from @aws-sdk/lib-storage, which splits the body into ~5 MB parts and
needs no content length up front.
size is informational and best-effort — it is undefined when the source reports no
Content-Length, and it is also dropped when the response is compressed
(Content-Encoding: gzip/br/deflate), because the decompressed body no longer matches the
advertised length. Don’t require it: only pass it to an API that needs an explicit content
length, and make sure that path has a buffered fallback for when it is missing.
Limits & storage
- Writing into storage is capped. A stream passed to
ctx.files.writeis counted againstAP_MAX_FILE_SIZE_MB(Cloud: 10 MB, self-hosted default: 25 MB) while the bytes flow; exceeding it aborts the transfer and fails the step. See Limits. - Reading a streamed input is not capped. A
streaming: truefile input has noAP_MAX_FILE_SIZE_MBceiling — that is deliberate, since the point of the feature is to move files larger than the cap out to an external service. - Storage backend. Streaming end-to-end requires S3 file storage — see the callout at the top of this page.