Handling Files in Frends
How to handle incoming and outgoing files in Frends Processes.
File-based data exchange is one of the most common integration patterns — systems drop files into directories, other systems pick them up, transform them, and route them onward. Frends has strong support for this pattern, from watching directories and triggering Processes automatically, to reading, transforming, writing, and uploading files to remote servers. This guide walks through a typical file handling flow end to end: starting a Process when a file arrives, finding and reading the file, and finally writing and uploading the result. It also covers how to handle binary files, such as images or PDFs, where text-based reading and writing is not appropriate.
Prerequisites
To follow this guide you need a Frends Tenant with at least one Agent that has access to the file system path you intend to monitor. For remote server transfers you also need valid credentials or SSH keys for the target SFTP or FTP server. The Tasks referenced here belong to the Frends.Files, Frends.SFTP, and Frends.FTP packages — make sure these are available in your environment.
Triggering a Process on a New File
The most natural way to start a file handling Process is with the File Trigger. It monitors a local directory accessible by the Agent and starts a new Process Instance whenever one or more matching files appear and remain stable on disk. Configure the Directory to watch, a File filter wildcard pattern such as *.xml or order_*.csv to narrow which files are picked up, and a Quiet period in seconds to ensure the file is fully written before the Process reads it. The trigger output provides #trigger.data.filePaths with an array of full file paths and #trigger.data.files with just the file names, both of which you can pass directly to file-reading Tasks downstream.
When you need richer logic before starting the Process — such as waiting until both a data file and a corresponding control file are present, or you need to monitor an SFTP location — a Conditional Trigger is the better choice. A Conditional Trigger runs a Subprocess on a schedule and starts the main Process only when the Subprocess returns a non-empty result, giving you full control over the start condition. Refer to the Conditional Trigger reference documentation for full details.
Finding and Reading the File
Once the Process has started, the next step is locating and reading the file content.
Finding Files Programmatically
When you need to search for files as part of the Process logic rather than relying solely on the trigger output, use the Frends.Files.Find Task. Its Directory parameter sets the root to search from, and Pattern supports a glob-style syntax where * matches within a single path segment, ? matches one character, and ** matches across multiple directory levels. For example, **\output\*\*.xml matches any XML file inside any output subfolder anywhere in the tree. The Task returns a list of file objects, each with properties like FullPath, FileName, Extension, SizeInMegaBytes, CreationTimeUtc, and LastWriteTimeUtc.
Note that Frends.Files.Find accepts only one pattern per Task call. If you need to match multiple patterns, use separate Find Tasks and combine their results downstream.
Downloading Files from a Remote Server
If the files reside on an SFTP server, use Frends.SFTP.DownloadFiles to transfer them to the Agent's local file system first. Set the Address, Port, and authentication parameters, then specify SourceDirectory and SourceFileMask to target the right files.
For the local destination, use a dedicated temporary folder that is not monitored by any File Trigger, to avoid the Process accidentally picking up its own intermediate files. On PaaS Agents, the appropriate locations are the shared Agent Group storage mounted as F:\ on Windows or /frends-shared-data/ on Linux, or the Agent-local storage mounted as G:\ on Windows or /frends-local-data/ on Linux. On self-hosted Agents, use a dedicated subfolder on a local or network-mounted path that the Agent service account has write access to.
The SourceOperation parameter controls what happens to the remote file after a successful download — common choices are Delete (remove from the server) or Move (archive to another remote folder). For FTP servers, the equivalent is Frends.FTP.DownloadFiles, which additionally has a TransportType setting of Ascii or Binary; use Binary for any file that is not plain text.
Reading a Text File
To read the content of a local text file, use Frends.Files.Read. Set Path to the file's full path — typically from #trigger.data.filePaths[0] or the FullPath property of a Find result. Choose the correct FileEncoding to match the file's actual encoding; options include UTF8, ASCII, Unicode, and Windows1252. If you need a less common encoding such as iso-8859-1, select Other and provide the encoding name in EncodingInString. The Task returns an object whose Content property holds the file text as a string, ready for transformation, parsing, or mapping in subsequent Tasks.
For files that live directly on an SFTP server without a prior download step, Frends.SFTP.ReadFile reads the file in a single Task and returns its content as either TextContent or BinaryContent depending on the ContentType setting you choose.
Writing and Uploading the Result
After your Process has processed or transformed the data, write the output to a file and optionally push it to a remote server.
Writing a Text File Locally
Frends.Files.Write Task writes a string to a local file. Provide the output string in Content, a destination path in Path, and pick a FileEncoding that matches what the downstream system expects. When writing an intermediate file before uploading it onward, write it to a dedicated temporary folder rather than a monitored inbox or the final outbox directly — this prevents half-written files from being picked up prematurely by another Process or a File Trigger.
On PaaS Agents, use the shared storage at F:\ (Windows) or /frends-shared-data/ (Linux) for files that need to be accessible across multiple Agents in the same group, or the Agent-local storage at G:\ (Windows) or /frends-local-data/ (Linux) for files only needed by the current Agent. On self-hosted Agents, use a dedicated subfolder on a path the Agent service account controls.
The WriteBehaviour parameter determines what happens if a file already exists at that path: Overwrite replaces it, Append adds to the end, and Throw raises an error so you can handle the conflict explicitly.
Uploading to an SFTP Server
Frends.SFTP.UploadFiles Task transfers one or more local files to an SFTP server. Point SourceFilePaths at the local files — you can feed #trigger.data.filePaths directly here if you want to forward the incoming files unchanged. Set TargetDirectory to the remote destination path. The TargetFileName parameter supports macros: %SourceFileName% inserts the original file name without extension, and %SourceFileExtension% inserts the extension including the dot, making it straightforward to preserve the original file name on the remote side.
SourceOperation applies to the local source file after a successful upload and supports Delete, Move, Rename, or Nothing. Enable CreateTargetDirectories if the remote directory might not exist yet. For reliable transfers, RenameTargetFileDuringTransfer uploads the file under a temporary name and renames it only once the transfer completes, preventing downstream systems from picking up a partial file.
For FTP, the equivalent Task is Frends.FTP.UploadFiles. Set TransportType to Binary when uploading anything other than plain ASCII text, including UTF-8 encoded files.
Handling Binary Files
Not all files are text. Images, PDFs, Office documents, and compressed archives need to be treated as raw byte sequences — reading them as text would corrupt the content.
Reading Binary Files
Use Frends.Files.ReadBytes Task instead of Frends.Files.Read when working with binary content. It shares the same Path parameter but returns a ContentBytes property of type byte[] rather than a string. Downstream Tasks receive the byte array and can pass it on, transform it, or write it to another file without any encoding conversion taking place.
For large binary files it is good practice to enable Skip logging results and parameters and Dispose at end of scope in the Task's advanced settings. This keeps the byte array out of the Process log and releases memory as soon as the Task result is no longer needed.
Writing Binary Files
The counterpart to ReadBytes is Frends.Files.WriteBytes. Its ContentBytes parameter accepts a byte[] directly. It also supports the same WriteBehaviour options as the text-based Write Task: Overwrite, Append, or Throw. There is no encoding parameter, because binary data is written exactly as-is.
Working with Base64-Encoded Binary Data
Some integration points — particularly API Triggers configured to receive raw request bodies, or REST APIs that exchange file data as JSON — represent binary content as a Base64-encoded string rather than a native byte array. Frends handles both conversion directions using standard .NET expressions in any expression field.
To decode a Base64 string into a byte[] for use with Frends.Files.WriteBytes or any Task expecting binary input, use Convert.FromBase64String:
To go the other direction — for example when you have read a file with Frends.Files.ReadBytes and need to pass its content as a Base64 string to an API or include it in a JSON payload — use Convert.ToBase64String:
Both expressions can be used directly in Task input fields or inside a larger expression, such as building a JSON string that embeds the encoded file.
Converting Between Strings and Byte Arrays
Sometimes you need to convert a plain text string into a byte[] — for instance, when you have built an XML or JSON string in the Process and need to pass it to Frends.Files.WriteBytes, or to a Task that expects binary input. Use System.Text.Encoding with the appropriate encoding for the content:
To go the other direction and decode a byte[] back into a readable string — for example after reading a text-based file with Frends.Files.ReadBytes — use GetString:
Replace UTF8 with ASCII, Unicode, or Latin1 as needed to match the actual encoding of the content. Choosing the wrong encoding here will produce garbled characters, so make sure it matches whatever the originating system wrote.
Uploading Binary Files
Binary files are uploaded with the same Frends.SFTP.UploadFiles or Frends.FTP.UploadFiles Tasks described earlier. For FTP, ensure TransportType is set to Binary. For SFTP, the protocol handles binary transfer by default, so no extra configuration is required beyond pointing the Task at the correct source file path.
Handling Binary Data over HTTP
Binary files often move over HTTP — either received as an incoming request body or fetched from a remote API. Frends provides two dedicated Tasks for this: Frends.HTTP.SendBytes for sending binary data and Frends.HTTP.RequestBytes for fetching binary data from a remote endpoint.
When sending a binary file as an HTTP request body, use Frends.HTTP.SendBytes. Set Content to the byte[] to send and configure the Uri, Method, and any required Headers such as Content-Type: application/pdf or Content-Type: application/octet-stream for a generic binary. The Task sends the raw bytes without any encoding transformation.
When fetching binary data from a remote API, use Frends.HTTP.RequestBytes. It behaves like a regular HTTP request Task but returns the response body as a byte[] in the result's BodyBytes property. You can pass that value directly to Frends.Files.WriteBytes to save the file locally, or convert it to a Base64 string with Convert.ToBase64String for embedding in a downstream payload. When your Process receives binary data through an HTTP Trigger or API Trigger configured to accept a raw request body, the incoming bytes are available as a Base64-encoded string via #trigger.data.httpContentBytesInBase64. Decode it back to a byte[] with Convert.FromBase64String before writing or processing the file.
Last updated
Was this helpful?

