Save File
Write incoming data to disk as an image, JSON, text, or binary file. The node can auto-detect the best mode from the payload or a provided filename/extension.
Inputs
- payload (default): Data to write. Supports raw image objects (
{data,width,height,channels,colorSpace}), encoded image buffers, plain objects/arrays, strings, or buffers. - Alternative path: Use the Input field to read from any
msg/flow/globalproperty.
Properties
Single File Mode (default)
- Folder (str | env | msg | flow | global) -- Destination directory. Created if missing.
- Filename (str | msg | flow | global) -- Optional name without path separators. If left blank, a timestamped name is generated. If you include an extension here, it is respected.
- File Type (auto | image | json | text | binary) -- Force the save mode or let the node decide. Auto uses the extension, raw image structure, or Sharp metadata for buffers.
- Image Format (jpg | png | webp | bmp) -- Encoding for image saves.
- Quality -- JPEG/WebP quality (1-100, default 90).
- PNG Compression -- PNG compression level (0-9, default 0). Level 0 uses a fast raw encoder; higher levels use Sharp for better compression at the cost of speed.
- WebP Options -- Lossless mode, smart subsample, and effort level (0-6) for fine-tuned WebP output.
- JSON Indent -- Spacing for pretty-printed JSON output.
- Overwrite protection -- Enabled by default; adds a numeric suffix when the file already exists.
- Max Files (num | msg | flow | global) -- Retention limit. When set, the oldest files in the folder are deleted to stay within the limit.
- Result to -- Where to store summary metadata
{ path, filename, type, extension, sizeBytes, format? }(defaultmsg.savedFile).
Multiple Files Mode
Save several files from a single message by switching to Multiple mode.
- Save Config (msg | flow | global) -- Path to an array of items to save (default
msg.payload). A single object is automatically wrapped in a one-item array. An empty or invalid config logs a warning and passes the message through without saving. - Image Field -- Property name within each item that holds the data to save (default
bitmap). Despite the name, it accepts anything single mode does -- raw image objects, encoded buffers, JSON, text, or binary -- with auto-detection applied per item. - Filename Field (item | str | msg | flow | global) -- Where to read the filename for each file. When set to
item, it is read from each array element (default keyfilename); otherwise a shared value is resolved once from the selected source. Blank names get a per-item timestamped default; an extension in the name drives type/format detection, as in single mode. - Output Dir Field (item | str | msg | flow | global) -- Where to read the output directory for each file, with the same resolution logic. Items can target different folders; each folder is created if missing.
Example message with all defaults (Save Config = msg.payload):
js
msg.payload = [
{ bitmap: <buffer|raw image>, filename: "crop_1.png", outputDir: "/data/out" },
{ bitmap: <buffer|raw image>, filename: "crop_2.png", outputDir: "/data/out" }
]Notes:
- File Type, Image Format, quality/compression options, JSON Indent, Overwrite protection, Max Files, and the Disk Limit are node-level settings applied to every item; the only per-item way to vary the format is the filename extension.
- Items are saved sequentially (not in parallel) so overwrite protection and Max Files enforcement stay consistent.
- The output metadata is an array of result objects for the saved files only -- skipped items are absent, so do not assume 1:1 correspondence with the input array.
Supported Image Formats
| Format | Notes |
|---|---|
| JPEG | Lossy, configurable quality. Default format. |
| PNG | Lossless, configurable compression level (0-9). |
| WebP | Lossy or lossless, with quality, smart subsample, and effort options. |
| BMP | Uncompressed bitmap. Supports 8-bit grayscale, 24-bit BGR, and 32-bit BGRA. Uses a fast raw encoder (no Sharp dependency). |
Behavior
- Nothing in this node blocks the event loop: all filesystem access is asynchronous, Sharp encodes on its own thread pool, and every pure-JavaScript pixel operation runs on a worker thread.
- Large raw images are copied once into shared memory, already in the row layout the encoder needs. The copy is time-budgeted (it yields to the event loop every 0.5 ms), so a 4K frame never holds the loop for more than a couple of milliseconds.
- Raw image inputs are validated and encoded with Sharp; BGR/BGRA channels are swapped in place on a worker thread.
- BMP encoding uses a raw encoder (on a worker thread) that writes pixels in BGR order directly, avoiding Sharp overhead. The pixel rows are written straight from shared memory: zero copies.
- PNG at compression level 0 uses a raw encoder with native CRC32 for maximum throughput, also on a worker thread. The file is written as parts (header, zlib stream, trailer) so nothing is concatenated.
- One worker thread is always kept warm; more are created lazily up to
min(4, cores - 1)(override with theRP_CPU_WORKERSenvironment variable) and shut down after 60 s idle. - Encoded image buffers are probed once with Sharp; the detected format is reused by the encoder.
- In multiple mode, up to 4 items are saved in parallel; results keep the item order.
- Writes to the same path are serialised, so concurrent messages or items can never interleave inside one file. With overwrite protection on, filenames are reserved before writing, so concurrent saves always get unique
_<n>suffixes. - Folder creation and folder scans are cached per folder; a folder removed externally is recreated on the next save. Disk usage (
statfs) is cached for 1 s per folder. - Context reads and writes (
flow/global) use Node-RED's asynchronous context API, so asynchronous context stores are supported. - JSON inputs are pretty-printed; valid JSON strings are kept as-is.
- Text mode writes stringified content; binary mode writes buffers directly (or stringified when not a buffer).
- When overwrite protection is enabled, the node keeps adding
_<n>until a free filename is found. - Disk usage is checked before each write; saves are skipped when usage exceeds the configured Disk Limit (default 90%, 0 disables the check).
- In multiple mode, failures on individual items are logged as warnings and skipped; the node only errors if all items fail.
Status
- Shows the final filename, duration, and whether an overwrite was avoided.
- In multiple mode, shows the count of saved files and any skipped items.
Example Flows
- Save final inference results as
results.jsonin/data/out. - Persist camera frames as WebP by setting File Type = image and Image Format = webp.
- Archive arbitrary payloads by pointing Input to
msg.myBufferand enabling overwrite protection. - Save multiple detection crops from a single message by switching to Multiple mode and pointing Save Config at
msg.detections.