Files
Directory-based integration: the CMS reads files the source drops into a folder (FILE_WATCH) and
writes files into a folder for systems that only consume files (FILE_WRITE). This is the path for
scales, chromatographs, spectrometers, LIMS, SPC and legacy systems that export CSV/TXT/XML.
Connection
Register the directory under External Connections with type File:
| Field | Purpose |
|---|---|
| Base Directory | Path as the CMS server sees it — in a container, the volume mount point |
| Validate write access | The connection test and Keep Alive also write and delete a temporary file |
The CMS does not speak SMB/NFS: a network share is mounted by the operating system (or by the
container volume) and from there on it is a local path. docker-compose.yml mounts a single root
at /data/arquivos (variable CMS_FILES_ROOT); each connection points to a subfolder of it, so a
new integration never requires touching the compose file.
Every path configured in the Collection and Delivery is relative to the Base Directory, and
escaping it with .. is blocked — including when the file name comes from a payload {{alias}}.
You see the full path
Since you only type the relative part, every folder or file field shows the full server path right below it, with the Base Directory greyed out and the part you control highlighted. It is the same pattern as the URL preview on an HTTP Delivery.
The listing does the same for the three paths of a Collection — watched folder, processed folder
and error folder. The last two are relative to the Base Directory, not to the watched folder:
that is how the server resolves them, and it was exactly what nobody could guess. Left blank, they
default to processados and erro.
If the Application’s file Connection has no Base Directory filled in, the preview gives way to a warning — with no root there is no full path, and the read or write will fail on the first run.
Collection — FILE_WATCH
The scan runs on every Scan Interval, filtering by File Mask (*.csv;*.txt, accepting *
and ?). Each eligible file becomes one or many messages in the collection queue.
| Field | Purpose |
|---|---|
| Stability (ms) | The file is only read after keeping the same size for this long — avoids reading something still being written |
| Maximum Size (KB) | Larger files go straight to the error folder, raising a Collection Error alert |
| Emission Mode | Whole file (1 message, raw content) or One message per line (with source metadata) |
| Encoding | UTF-8, Latin-1, UTF-16 LE, ASCII or Binary (Base64) |
| Action After Reading | Move, Rename, Delete or None |
In one message per line mode the payload is always a JSON envelope:
{ "arquivo": "batch.csv", "caminho": "/data/arquivos/lab/input/batch.csv", "linha": 42,
"conteudo": "A;B;C",
"modificadoEm": "2026-08-21T10:00:00.000Z", "recebidoEm": "2026-08-21T10:00:03.120Z" }In whole file mode the payload is the raw content (XML/JSON reach the Transformer ready to use), unless Include metadata is turned on.
The None action keeps track of what was already read only in the CMS memory: restarting the service collects every file still in the folder again. Use it for testing only — with the other actions it is the filesystem itself that guarantees nothing is collected twice.
There is no native CSV/XML parser: convert it with a Transformer, as everywhere else in the CMS.
Detect from a file
The Detect from a file button reads a sample of a file already in the folder and fills in encoding, read mode, header and mask. Nothing in the file is changed.
It is the same detection used by the folder catalog, and it matters for the same reason: encoding and delimiter are exactly the fields nobody gets right first time and that only fail on the first real file.
Concurrency and failures
Before reading, the file is renamed to <name>.cms-processando — an atomic rename that prevents two
CMS instances pointing at the same folder from collecting the same file. If reading or persisting the
message fails, the file goes to the Error Folder and raises ERRO_COLETA. An unreachable
directory marks the collection as disconnected and raises CONEXAO_ARQUIVO_PERDIDA, without stopping
the other collections.
Delivery — FILE_WRITE
The File Path/Name is the Definition’s URI Post Message field, relative to the Base Directory
and accepting payload {{alias}} — the same mechanism as the MQTT topic:
output/{{orderNumber}}.json| Field | Purpose |
|---|---|
| Write Mode | Create new (fails if it already exists), Overwrite or Append |
| Create directory | Creates the destination folder when it does not exist yet |
| Atomic write | Writes to a .tmp and renames at the end, so the consumer never reads a half-written file |
Content Model
Defines what goes inside the file. Left empty, it writes the message payload as it arrived. Filled
in, it writes that text with every {{alias}} replaced by the payload value — and there is a subtlety
worth knowing here:
| Field | Resolves against |
|---|---|
| File Path/Name | The original payload, before the Transformer |
| Content Model | The already transformed payload, i.e. the Transformer’s output |
Use the escape filters whenever the value lands inside a string: {{alias|json}},
{{alias|csv}}, {{alias|xml}}. Without them, a quote or a line break in the data breaks the
generated file — and the defect only surfaces later, in the legacy system that reads it.
The Validate generated content option (on by default) checks, before writing, that the result is
still a valid document for the Delivery’s Content-Type (JSON or XML). If it is not, the delivery fails
with a clear error and retries, instead of silently writing a corrupted file. Content-Type
text/plain has nothing to validate.
In Append mode, end the model with a line break so each message becomes one line of the file.
The value stored in payloadRetorno is {"caminho": "<full path of the written file>", "bytes": N} —
JSON (rather than plain text) because this return value can be forwarded and read by a Transformer as
{{caminho}}.