Specification: Ballerina FTP Library

Owners: @shafreenAnfar @dilanSachi @Bhashinee
Reviewers: @shafreenAnfar @Bhashinee
Created: 2020/10/28
Updated: 2025/11/20
Edition: Swan Lake

Introduction

This is the specification for the FTP standard library of Ballerina language, which provides FTP client/listener functionalities to send and receive files by connecting to FTP/SFTP server.

The FTP library specification has evolved and may continue to evolve in the future. The released versions of the specification can be found under the relevant Github tag.

If you have any feedback or suggestions about the library, start a discussion via a GitHub issue or in the Discord server. Based on the outcome of the discussion, the specification and implementation can be updated. Community feedback is always welcome. Any accepted proposal, which affects the specification is stored under /docs/proposals. Proposals under discussion can be found with the label type/proposal in GitHub.

The conforming implementation of the specification is released and included in the distribution. Any deviation from the specification is considered a bug.

Contents

1. Overview

FTP is a file transfer protocol. It’s a basic way of using the Internet to share files. SFTP (or Secure File Transfer Protocol) is an alternative to FTP that also allows transferring files, but adds a layer of security to the process. SFTP uses SSH (or secure shell) encryption to protect data as it’s being transferred. This means data is not exposed to outside entities on the Internet when it is sent to another party. This library provides support for both protocols.

Ballerina FTP library contains two core apis:

  • Client - The ftp:Client is used to connect to FTP server and perform various operations on the files.
  • Listener - The ftp:Listener is used to listen to a remote FTP location and notify if files are added or removed from the FTP location.

2. Configurations

2.1. Security Configurations

public type PrivateKey record {|
    # Path to the private key file
    string path;
    # Private key password
    string password?;
|};
  • Credentials record represents the username and password configurations.
public type Credentials record {|
    # Username of the user
    string username;
    # Password of the user
    string password?;
|};
  • AuthConfiguration record represents the configurations needed for facilitating secure communication with a remote FTP server.
public type AuthConfiguration record {|
    # Username and password to be used
    Credentials credentials?;
    # Private key to be used
    PrivateKey privateKey?;
    # Preferred authentication methods and their order of preference
    PreferredMethod[] preferredMethods?;
|};
  • PreferredMethod enum specifies the supported authentication methods.
public enum PreferredMethod {
    # Security key file authentication
    PUBLICKEY,
    # Username and password authentication
    PASSWORD,
    # Interactive authentication (question and answer)
    KEYBOARD_INTERACTIVE,
    # Enterprise authentication system
    GSSAPI_WITH_MIC
}

2.2. FileInfo

  • FileInfo record contains the metadata of the files.
public type FileInfo record {|
    # Relative file path for a newly-added file
    string path;
    # Size of the file
    int size;
    # Last-modified timestamp of the file in UNIX Epoch time
    int lastModifiedTimestamp;
    # File name
    string name;
    # `true` if the file is a folder
    boolean isFolder;
    # `true` if the file is a file
    boolean isFile;
    # Normalized absolute path of this file within its file system
    string pathDecoded;
    # Extension of the file name
    string extension;
    # Receiver as a URI String for public display
    string publicURIString;
    # Type of the file
    string fileType;
    # `true` if the `fileObject` is attached
    boolean isAttached;
    # `true` if someone reads/writes from/to this file
    boolean isContentOpen;
    # `true` if this file is executable
    boolean isExecutable;
    # `true` if this file is hidden
    boolean isHidden;
    # `true` if this file can be read
    boolean isReadable;
    # `true` if this file can be written
    boolean isWritable;
    # Depth of the file name within its file system
    int depth;
    # URI scheme of the file
    string scheme;
    # Absolute URI of the file
    string uri;
    # Root URI of the file system in which the file exists
    string rootURI;
    # A "friendly path" is a path, which can be accessed without a password
    string friendlyURI;
|};

3. Client

The ftp:Client connects to FTP server and performs various operations on the files. It supports reading files in multiple formats (bytes, text, JSON, XML, CSV) with streaming support for large files, writing files in multiple formats, and file management operations including create, delete, rename, move, copy, and list.

3.1. Configurations

  • When initializing the ftp:Client, ftp:ClientConfiguration configuration can be provided.
public type ClientConfiguration record {|
    # Supported FTP protocols
    Protocol protocol = FTP;
    # Target service URL
    string host = "127.0.0.1";
    # Port number of the remote service
    int port = 21;
    # Authentication options
    AuthConfiguration auth?;
    # If set to `true`, treats the login home directory as the root (`/`) and
    # prevents the underlying VFS from attempting to change to the actual server root.
    # If `false`, treats the actual server root as `/`, which may cause a `CWD /` command
    # that can fail on servers restricting root access (e.g., chrooted environments).
    boolean userDirIsRoot = false;
    # If set to `true`, allows missing or null values when reading files in structured formats
    boolean laxDataBinding = false;
|};
  • InputContent record represents the configurations for the input given for put and append operations.
public type InputContent record{|
    # Path of the file to be created or appended
    string filePath;
    # `true` if the input type is a file
    boolean isFile = false;
    # The content read from the input file, if the input is a file
    stream<byte[] & readonly, io:Error?> fileContent?;
    # The input content, for other input types
    string textContent?;
    # If true, input will be compressed before uploading
    boolean compressInput = false;
|};
  • Following Compression options can be used when adding a file to the FTP server.
public enum Compression {
    # Zip compression
    ZIP,
    # No compression used
    NONE
}
  • FileWriteOption enum specifies how a file should be written to the server.
public enum FileWriteOption {
    # Replace the entire file with new content
    OVERWRITE,
    # Add new content to the end of the file
    APPEND
}

3.2. Initialization

3.2.1. Insecure Client

A simple insecure client can be initialized by providing ftp:FTP as the protocol and the host and optionally, the port to the ftp:ClientConfiguration.

# Gets invoked during object initialization.
#
# + clientConfig - Configurations for FTP client
# + return - `ftp:Error` in case of errors or `()` otherwise
public isolated function init(ClientConfiguration clientConfig) returns Error?;

3.2.2. Secure Client

A secure client can be initialized by providing ftp:SFTP as the protocol and by providing ftp:Credentials and ftp:PrivateKey to ftp:AuthConfiguration.

ftp:ClientConfiguration ftpConfig = {
    protocol: ftp:SFTP,
    host: "<The FTP host>",
    port: <The FTP port>,
    auth: {
        credentials: {
            username: "<The FTP username>",
            password: "<The FTP passowrd>"
        }
    },
    userDirIsRoot: true
};

3.3. Functions

  • FTP Client API can be used to put files on the FTP server. For this, the put() method can be used.
# Adds a file to FTP server.
# ```ballerina
# ftp:Error? response = client->put(path, channel);
# ```
#
# + path - The resource path
# + content - Content to be written to the file in server
# + compressionType - Type of the compression to be used, if
#                     the file should be compressed before
#                     uploading
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function put(string path, stream<byte[] & readonly, io:Error?>|string|xml|json content, Compression compressionType=NONE) returns Error?;
  • append() can be used to append the content to an existing file in FTP server.
# Appends the content to an existing file in FTP server.
# ```ballerina
# ftp:Error? response = client->append(path, content);
# ```
#
# + path - The resource path
# + content - Content to be written to the file in server
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function append(string path, stream<byte[] & readonly, io:Error?>|string|xml|json content) returns Error?;
  • putBytes() can be used to write bytes to a file.
# Write bytes to a file.
# ```ballerina
# ftp:Error? response = client->putBytes(path, content, option);
# ```
#
# + path - File location on the server
# + content - Binary data to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putBytes(string path, byte[] content, FileWriteOption option = OVERWRITE) returns Error?;
  • putText() can be used to write text to a file.
# Write text to a file.
# ```ballerina
# ftp:Error? response = client->putText(path, content, option);
# ```
#
# + path - File location on the server
# + content - Text to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putText(string path, string content, FileWriteOption option = OVERWRITE) returns Error?;
  • putJson() can be used to write JSON data to a file.
# Write JSON data to a file.
# ```ballerina
# ftp:Error? response = client->putJson(path, content, option);
# ```
#
# + path - File location on the server
# + content - JSON data to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putJson(string path, json|record {} content, FileWriteOption option = OVERWRITE) returns Error?;
  • putXml() can be used to write XML data to a file.
# Write XML data to a file.
# ```ballerina
# ftp:Error? response = client->putXml(path, content, option);
# ```
#
# + path - File location on the server
# + content - XML data to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putXml(string path, xml|record {} content, FileWriteOption option = OVERWRITE) returns Error?;
  • putCsv() can be used to write CSV data to a file.
# Write CSV data to a file.
# ```ballerina
# ftp:Error? response = client->putCsv(path, content, option);
# ```
#
# + path - File location on the server
# + content - CSV data (table or records) to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putCsv(string path, string[][]|record {}[] content, FileWriteOption option = OVERWRITE) returns Error?;
  • putBytesAsStream() can be used to write bytes from a stream to a file.
# Write bytes from a stream to a file.
# Useful for processing and uploading large files without loading everything into memory.
# ```ballerina
# ftp:Error? response = client->putBytesAsStream(path, content, option);
# ```
#
# + path - File location on the server
# + content - Stream of byte chunks to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putBytesAsStream(string path, stream<byte[], error?> content, FileWriteOption option = OVERWRITE) returns Error?;
  • putCsvAsStream() can be used to write CSV data from a stream to a file.
# Write CSV data from a stream to a file.
# Useful for processing and uploading large CSV files without loading everything into memory.
# ```ballerina
# ftp:Error? response = client->putCsvAsStream(path, content, option);
# ```
#
# + path - File location on the server
# + content - Stream of CSV rows to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putCsvAsStream(string path, stream<string[]|record {}, error?> content, FileWriteOption option = OVERWRITE) returns Error?;
  • To retrieve file content from FTP server, get() can be used.
# Retrieves the file content from a remote resource.
# ```ballerina
# stream<byte[] & readonly, io:Error?>|ftp:Error channel = client->get(path);
# ```
#
# + path - The resource path
# + return - A byte stream from which the file can be read or `ftp:Error` in case of errors
remote isolated function get(string path) returns stream<byte[] & readonly, io:Error?>|Error;
  • getBytes() can be used to read file content as bytes.
# Read file content as bytes (raw binary data).
# ```ballerina
# byte[] content = check client->getBytes(path);
# ```
#
# + path - File or folder location on the server
# + return - File content as bytes, or an error if the operation fails
remote isolated function getBytes(string path) returns byte[]|Error;
  • getText() can be used to read file content as text.
# Read file content as text.
# ```ballerina
# string content = check client->getText(path);
# ```
#
# + path - File or folder location on the server
# + return - File content as text, or an error if the operation fails
remote isolated function getText(string path) returns string|Error;
  • getJson() can be used to read a file as JSON data.
# Read a file as JSON data.
# ```ballerina
# json content = check client->getJson(path);
# ```
#
# + path - Location of the file on the server
# + targetType - What format should the data have? (JSON, structured data, or a custom format)
# + return - The file content as JSON or an error if the operation fails
remote isolated function getJson(string path, typedesc<json|record {}> targetType = <>) returns targetType|Error;
  • getXml() can be used to read a file as XML data.
# Read a file as XML data.
# ```ballerina
# xml content = check client->getXml(path);
# ```
#
# + path - Location of the file on the server
# + targetType - What format should the data have? (XML, structured data, or a custom format)
# + return - The file content as XML or an error if the operation fails
remote isolated function getXml(string path, typedesc<xml|record {}> targetType = <>) returns targetType|Error;
  • getCsv() can be used to read a CSV file from the server.
# Read a CSV (comma-separated) file from the server.
# The first row of the CSV file should contain column names (headers).
# ```ballerina
# string[][] content = check client->getCsv(path);
# ```
#
# + path - Location of the CSV file on the server
# + targetType - What format should the data have? (Table or structured records)
# + return - The CSV file content as a table or records, or an error if the operation fails
remote isolated function getCsv(string path, typedesc<string[][]|record {}[]> targetType = <>) returns targetType|Error;
  • getBytesAsStream() can be used to read file content as a stream of byte chunks.
# Read file content as a stream of byte chunks.
# Useful for processing large files without loading the entire file into memory.
# ```ballerina
# stream<byte[], error?> response = check client->getBytesAsStream(path);
# ```
#
# + path - File or folder location on the server
# + return - A continuous stream of byte chunks, or an error if the operation fails
remote isolated function getBytesAsStream(string path) returns stream<byte[], error?>|Error;
  • getCsvAsStream() can be used to read a CSV file as a continuous stream of rows.
# Read a CSV file as a continuous stream of rows.
# Useful for processing very large files one row at a time.
# The first row of the CSV file should contain column names (headers).
# ```ballerina
# stream<string[], error?> response = check client->getCsvAsStream(path);
# ```
#
# + path - Location of the CSV file on the server
# + targetType - What format should each row have? (Row values or structured record)
# + return - A stream of rows from the CSV file, or an error if the operation fails
remote isolated function getCsvAsStream(string path, typedesc<string[]|record {}> targetType = <>) returns stream<targetType, error?>|Error;
  • mkdir() can be used to create a new directory in FTP server.
# Creates a new directory in FTP server.
# ```ballerina
# ftp:Error? response = client->mkdir(path);
# ```
#
# + path - The directory path
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function mkdir(string path) returns Error?;
  • rmdir() can be used to remove an empty directory in the server.
# Deletes an empty directory in FTP server.
# ```ballerina
# ftp:Error? response = client->rmdir(path);
# ```
#
# + path - The directory path
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function rmdir(string path) returns Error?;
  • To delete a file, delete() can be used.
# Deletes a file from FTP server.
# ```ballerina
# ftp:Error? response = client->delete(path);
# ```
#
# + path - The resource path
# + return -  `()` or else an `ftp:Error` if failed to establish
#             the communication with the FTP server
remote isolated function delete(string path) returns Error?;
  • To rename a file or move it to another directory, rename() can be used.
# Renames a file or moves it to a new location within the same FTP server.
# ```ballerina
# ftp:Error? response = client->rename(origin, destination);
# ```
#
# + origin - The source file location
# + destination - The destination file location
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function rename(string origin, string destination) returns Error?;
  • move() can be used to move a file to a different location on the file server.
# Move a file to a different location on the file server.
# ```ballerina
# ftp:Error? response = client->move(sourcePath, destinationPath);
# ```
#
# + sourcePath - The current file location
# + destinationPath - The new file location
# + return - An error if the operation fails
remote isolated function move(string sourcePath, string destinationPath) returns Error?;
  • copy() can be used to copy a file to a different location on the file server.
# Copy a file to a different location on the file server.
# ```ballerina
# ftp:Error? response = client->copy(sourcePath, destinationPath);
# ```
#
# + sourcePath - The file to copy
# + destinationPath - Where to create the copy
# + return - An error if the operation fails
remote isolated function copy(string sourcePath, string destinationPath) returns Error?;
  • exists() can be used to check if a file or folder exists on the file server.
# Check if a file or folder exists on the file server.
# ```ballerina
# boolean|ftp:Error response = client->exists(path);
# ```
#
# + path - File or folder location to check
# + return - True if it exists, false if it doesn't, or an error if the check fails
remote isolated function exists(string path) returns boolean|Error;
  • size() function can be used to get the size of a file.
# Gets the size of a file resource.
# ```ballerina
# int|ftp:Error response = client->size(path);
# ```
#
# + path - The resource path
# + return - The file size in bytes or an `ftp:Error` if
#            failed to establish the communication with the FTP server
remote isolated function size(string path) returns int|Error;
  • To get the file list in a directory, list() can be used.
# Gets the file name list in a given folder.
# ```ballerina
# ftp:FileInfo[]|ftp:Error response = client->list(path);
# ```
#
# + path - The direcotry path
# + return - An array of file names or an `ftp:Error` if failed to
#            establish the communication with the FTP server
remote isolated function list(string path) returns FileInfo[]|Error;
  • To check if a resource is a directory, isDirectory() can be used.
# Checks if a given resource is a directory.
# ```ballerina
# boolean|ftp:Error response = client->isDirectory(path);
# ```
#
# + path - The resource path
# + return - `true` if given resource is a directory or an `ftp:Error` if
#            an error occurred while checking the path
remote isolated function isDirectory(string path) returns boolean|Error;

4. Listener

The ftp:Listener is used to listen to a remote FTP location and trigger a WatchEvent type of event when new files are added to or deleted from the directory. The onFileChange function is invoked when a new file is added and/or deleted.

4.1. Configurations

  • When initializing the ftp:Listener, following configurations can be provided.
public type ListenerConfiguration record {|
    # Supported FTP protocols
    Protocol protocol = FTP;
    # Target service url
    string host = "127.0.0.1";
    # Port number of the remote service
    int port = 21;
    # Authentication options
    AuthConfiguration auth?;
    # Remote FTP directory location
    string path = "/";
    # File name pattern that event need to trigger
    string fileNamePattern?;
    # Periodic time interval to check new update
    decimal pollingInterval = 60;
    # If set to `true`, treats the login home directory as the root (`/`) and
    # prevents the underlying VFS from attempting to change to the actual server root.
    # If `false`, treats the actual server root as `/`, which may cause a `CWD /` command
    # that can fail on servers restricting root access (e.g., chrooted environments).
    boolean userDirIsRoot = false;
    # If set to `true`, allows missing or null values when reading files in structured formats
    boolean laxDataBinding = false;
|};
  • WatchEvent record represents the latest status change of the server from the last status change.
public type WatchEvent record {|
    # Array of `ftp:FileInfo` that represents newly added files
    FileInfo[] addedFiles;
    # Array of strings that contains deleted file names
    string[] deletedFiles;
|};

4.2. Initialization

4.2.1. Insecure Listener

An insecure FTP listener can be initialized by providing the mandatory protocol, host, and path parameters to the ftp:ListenerConfiguration.

# Gets invoked during object initialization.
#
# + listenerConfig - Configurations for FTP listener
# + return - `()` or else an `ftp:Error` upon failure to initialize the listener
public isolated function init(*ListenerConfiguration listenerConfig) returns Error?;

4.2.2. Secure Listener

A secure listener can be initialized by providing ftp:SFTP as the protocol and by providing ftp:Credentials and ftp:PrivateKey to ftp:AuthConfiguration.

ftp:ListenerConfiguration ftpConfig = {
    protocol: ftp:SFTP,
    host: "<The SFTP host>",
    port: <The SFTP port>,
    path: "<The remote SFTP directory location>",
    pollingInterval: <Polling interval>,
    fileNamePattern: "<File name pattern>",
    auth: {
        credentials: {username: "<The SFTP username>", password: "<The SFTP password>"},
        privateKey: {
            path: "<The private key file path>",
            password: "<The private key file password>"
        }
    },
    userDirIsRoot: true
};

4.3. Usage

After initializing the listener, a service must be attached to the listener. There are two ways for this.

  1. Attach the service to the listener directly.
service ftp:Service on ftpListener {
    remote function onFileChange(ftp:WatchEvent & readonly event, ftp:Caller caller) {
        // process event
    }
}
  1. Attach the service dynamically.
// Create a service object
ftp:Service ftpListener = service object {
    remote function onFileChange(ftp:WatchEvent & readonly event, ftp:Caller caller) {
        // process event
    }
};

The remote method onFileChange() is invoked when the listener notices a file change in the FTP server. This function supports having both ftp:WatchEvent and ftp:Caller parameters or having only ftp:WatchEvent parameter.

4.3.1. Format-Specific Listener Callbacks

In addition to the generic onFileChange() callback, the listener supports specialized format-specific callbacks that automatically parse files into structured data formats. These callbacks simplify handling files of specific types.

File extension routing: Files are automatically routed to handlers based on their extensions. .txtonFileText(), .jsononFileJson(), .xmlonFileXml(), .csvonFileCsv(). Other extensions use onFile(). Routing can be customized using the @ftp:FunctionConfig annotation.

  • onFileJson() - Triggered when a JSON file (.json) is added. Supports two overloads for different content types:
# JSON content overload
remote function onFileJson(json content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process parsed JSON content directly
    // fileInfo contains metadata about the file
    // caller allows you to perform FTP operations
}

# Data-bound record overload
remote function onFileJson(record {} content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process JSON automatically data-bound to your record type
}
  • onFileXml() - Triggered when an XML file (.xml) is added. Supports two overloads:
# XML content overload
remote function onFileXml(xml content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process parsed XML content directly
}

# Data-bound record overload
remote function onFileXml(record {} content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process XML automatically data-bound to your record type
}
  • onFileCsv() - Triggered when a CSV file (.csv) is added with RFC4180 defaults. Supports four overloads for different processing modes:
# String array overload (in-memory, all rows)
remote function onFileCsv(string[][] content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // content contains all rows as arrays of strings
    // First row contains column headers
}

# Record array overload (in-memory, type-safe)
remote function onFileCsv(record {} [] content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // content automatically data-bound to record array
    // Headers automatically mapped to record fields
}

# Stream of string arrays (streaming, large files)
remote function onFileCsv(stream<string[], error> content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process rows one at a time from stream
    // Memory-efficient for large files
}

# Stream of records (streaming, type-safe)
remote function onFileCsv(stream<record {}, error> content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process rows as records from stream
    // Data-bound and memory-efficient
}
  • onFileText() - Triggered when a text file (.txt) is added:
remote function onFileText(string content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process entire file content as UTF-8 text
}
  • onFile() - Triggered when any other file type is added (generic binary handling). Supports two overloads:
# In-memory byte array overload
remote function onFile(byte[] content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // content contains entire file as bytes
}

# Streaming overload for large files
remote function onFile(stream<byte[], error> content, ftp:FileInfo fileInfo, ftp:Caller caller) returns error? {
    // Process file chunks from stream
    // Memory-efficient for large files
}
  • onFileDeleted() - Triggered when files are deleted from the monitored directory:
remote function onFileDeleted(string[] deletedFiles, ftp:Caller caller) returns error? {
    // Handle file deletion
    // deletedFiles contains array of deleted file paths
}

Optional parameters: The fileInfo and caller parameters can be omitted if not needed in your implementation.

All format-specific callbacks receive fileInfo (metadata about the file) and optionally caller (to perform additional FTP operations). The data is automatically parsed based on the callback type.

The Listener has following functions to manage a service.

  • attach() - can be used to bind a service to the ftp:Listener.
# Binds a service to the `ftp:Listener`.
# ```ballerina
# error? response = listener.attach(service1);
# ```
#
# + ftpService - Service to be detached from the listener
# + name - Name of the service to be detached from the listener
# + return - `()` or else an `error` upon failure to register the listener
public isolated function attach(Service ftpService, string[]|string? name = ()) returns error?;
  • detach() - can be used to detach a service from the ftp:Listener.
# Stops consuming messages and detaches the service from the `ftp:Listener`.
# ```ballerina
# error? response = listener.detach(service1);
# ```
#
# + ftpService - Service to be detached from the listener
# + return - `()` or else an `error` upon failure to detach the service
public isolated function detach(Service ftpService) returns error?;
  • start() - needs to be called to start the listener.
# Starts the `ftp:Listener`.
# ```ballerina
# error? response = listener.'start();
# ```
#
# + return - `()` or else an `error` upon failure to start the listener
public isolated function 'start() returns error?;
  • gracefulStop() - can be used to gracefully stop the listener.
# Stops the `ftp:Listener` gracefully.
# ```ballerina
# error? response = listener.gracefulStop();
# ```
#
# + return - `()` or else an `error` upon failure to stop the listener
public isolated function gracefulStop() returns error?;
  • immediateStop() - can be used to forcefully stop the listener.
# Stops the `ftp:Listener` forcefully.
# ```ballerina
# error? response = listener.immediateStop();
# ```
#
# + return - `()` or else an `error` upon failure to stop the listener
public isolated function immediateStop() returns error?;
  • poll() - can be used to poll new files from the FTP server.
# Poll new files from a FTP server.
# ```ballerina
# error? response = listener.poll();
# ```
#
# + return - An `error` if failed to establish communication with the FTP
#            server
public isolated function poll() returns error?
  • register() can be used to register an FTP service in an ftp:listener.
# Register a FTP service in an FTP listener server.
# ```ballerina
# error? response = listener.register(ftpService, name);
# ```
#
# + ftpService - The FTP service
# + name - Name of the FTP service
# + return - An `error` if failed to establish communication with the FTP
#            server
public isolated function register(Service ftpService, string? name) returns error?

5. Caller

ftp:Caller is like a wrapper on the ftp:Client. It has an ftp:Client defined inside and contains all the APIs of ftp:Client like get(), put(), append() etc. However, ftp:Caller can only be created internally to be passed to the onFileChange method. ftp:Caller is created in the runtime when the ftp:Listener gets attached to a ftp:Service by checking whether the user has added ftp:Caller as a parameter in the onFileChange method.

5.1. Initialization

ftp:Caller can be both secure and insecure and this depends on the type of ftp:Listener. If the ftp:Listener is a secure type, ftp:Caller will also be secure since the wrapping ftp:Client is created using a subset of the ftp:ListenerConfiguration.

# Gets invoked during object initialization.
#
# + 'client - The `ftp:Client` which is used to interact with the Ftp server
# + return - `ftp:Error` in case of errors or `()` otherwise
isolated function init(Client 'client) {
    self.'client = 'client;
}

5.2. Functions

  • put() method can be used to put files on the server.
# Adds a file to FTP server.
# ```ballerina
# ftp:Error? response = caller->put(path, channel);
# ```
#
# + path - The resource path
# + content - Content to be written to the file in server
# + compressionType - Type of the compression to be used, if
#                     the file should be compressed before
#                     uploading
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function put(string path, stream<byte[] & readonly, io:Error?>|string|xml|json content, Compression compressionType=NONE) returns Error?;
  • append() can be used to append the content to an existing file in FTP server.
# Appends the content to an existing file in FTP server.
# ```ballerina
# ftp:Error? response = caller->append(path, content);
# ```
#
# + path - The resource path
# + content - Content to be written to the file in server
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function append(string path, stream<byte[] & readonly, io:Error?>|string|xml|json content) returns Error?;
  • putBytes() can be used to write bytes to a file.
# Write bytes to a file.
# ```ballerina
# ftp:Error? response = caller->putBytes(path, content, option);
# ```
#
# + path - File location on the server
# + content - Binary data to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putBytes(string path, byte[] content, FileWriteOption option = OVERWRITE) returns Error?;
  • putText() can be used to write text to a file.
# Write text to a file.
# ```ballerina
# ftp:Error? response = caller->putText(path, content, option);
# ```
#
# + path - File location on the server
# + content - Text to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putText(string path, string content, FileWriteOption option = OVERWRITE) returns Error?;
  • putJson() can be used to write JSON data to a file.
# Write JSON data to a file.
# ```ballerina
# ftp:Error? response = caller->putJson(path, content, option);
# ```
#
# + path - File location on the server
# + content - JSON data to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putJson(string path, json|record {} content, FileWriteOption option = OVERWRITE) returns Error?;
  • putXml() can be used to write XML data to a file.
# Write XML data to a file.
# ```ballerina
# ftp:Error? response = caller->putXml(path, content, option);
# ```
#
# + path - File location on the server
# + content - XML data to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putXml(string path, xml|record {} content, FileWriteOption option = OVERWRITE) returns Error?;
  • putCsv() can be used to write CSV data to a file.
# Write CSV data to a file.
# ```ballerina
# ftp:Error? response = caller->putCsv(path, content, option);
# ```
#
# + path - File location on the server
# + content - CSV data (table or records) to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putCsv(string path, string[][]|record {}[] content, FileWriteOption option = OVERWRITE) returns Error?;
  • putBytesAsStream() can be used to write bytes from a stream to a file.
# Write bytes from a stream to a file.
# Useful for processing and uploading large files without loading everything into memory.
# ```ballerina
# ftp:Error? response = caller->putBytesAsStream(path, content, option);
# ```
#
# + path - File location on the server
# + content - Stream of byte chunks to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putBytesAsStream(string path, stream<byte[], error?> content, FileWriteOption option = OVERWRITE) returns Error?;
  • putCsvAsStream() can be used to write CSV data from a stream to a file.
# Write CSV data from a stream to a file.
# Useful for processing and uploading large CSV files without loading everything into memory.
# ```ballerina
# ftp:Error? response = caller->putCsvAsStream(path, content, option);
# ```
#
# + path - File location on the server
# + content - Stream of CSV rows to write
# + option - Replace or add to the file?
# + return - An error if the operation fails
remote isolated function putCsvAsStream(string path, stream<string[]|record {}, error?> content, FileWriteOption option = OVERWRITE) returns Error?;
  • To retrieve file content from FTP server, get() can be used.
# Retrieves the file content from a remote resource.
# ```ballerina
# stream<byte[] & readonly, io:Error?>|ftp:Error channel = caller->get(path);
# ```
#
# + path - The resource path
# + return - A byte stream from which the file can be read or `ftp:Error` in case of errors
remote isolated function get(string path) returns stream<byte[] & readonly, io:Error?>|Error;
  • getBytes() can be used to read file content as bytes.
# Read file content as bytes (raw binary data).
# ```ballerina
# byte[] content = check caller->getBytes(path);
# ```
#
# + path - File or folder location on the server
# + return - File content as bytes, or an error if the operation fails
remote isolated function getBytes(string path) returns byte[]|Error;
  • getText() can be used to read file content as text.
# Read file content as text.
# ```ballerina
# string content = check caller->getText(path);
# ```
#
# + path - File or folder location on the server
# + return - File content as text, or an error if the operation fails
remote isolated function getText(string path) returns string|Error;
  • getJson() can be used to read a file as JSON data.
# Read a file as JSON data.
# ```ballerina
# json content = check caller->getJson(path);
# ```
#
# + path - Location of the file on the server
# + targetType - What format should the data have? (JSON, structured data, or a custom format)
# + return - The file content as JSON or an error if the operation fails
remote isolated function getJson(string path, typedesc<json|record {}> targetType = <>) returns targetType|Error;
  • getXml() can be used to read a file as XML data.
# Read a file as XML data.
# ```ballerina
# xml content = check caller->getXml(path);
# ```
#
# + path - Location of the file on the server
# + targetType - What format should the data have? (XML, structured data, or a custom format)
# + return - The file content as XML or an error if the operation fails
remote isolated function getXml(string path, typedesc<xml|record {}> targetType = <>) returns targetType|Error;
  • getCsv() can be used to read a CSV file from the server.
# Read a CSV (comma-separated) file from the server.
# The first row of the CSV file should contain column names (headers).
# ```ballerina
# string[][] content = check caller->getCsv(path);
# ```
#
# + path - Location of the CSV file on the server
# + targetType - What format should the data have? (Table or structured records)
# + return - The CSV file content as a table or records, or an error if the operation fails
remote isolated function getCsv(string path, typedesc<string[][]|record {}[]> targetType = <>) returns targetType|Error;
  • getBytesAsStream() can be used to read file content as a stream of byte chunks.
# Read file content as a stream of byte chunks.
# Useful for processing large files without loading the entire file into memory.
# ```ballerina
# stream<byte[], error?> response = check caller->getBytesAsStream(path);
# ```
#
# + path - File or folder location on the server
# + return - A continuous stream of byte chunks, or an error if the operation fails
remote isolated function getBytesAsStream(string path) returns stream<byte[], error?>|Error;
  • getCsvAsStream() can be used to read a CSV file as a continuous stream of rows.
# Read a CSV file as a continuous stream of rows.
# Useful for processing very large files one row at a time.
# The first row of the CSV file should contain column names (headers).
# ```ballerina
# stream<string[], error?> response = check caller->getCsvAsStream(path);
# ```
#
# + path - Location of the CSV file on the server
# + targetType - What format should each row have? (Row values or structured record)
# + return - A stream of rows from the CSV file, or an error if the operation fails
remote isolated function getCsvAsStream(string path, typedesc<string[]|record {}> targetType = <>) returns stream<targetType, error?>|Error;
  • mkdir() can be used to create a new directory in FTP server.
# Creates a new directory in FTP server.
# ```ballerina
# ftp:Error? response = caller->mkdir(path);
# ```
#
# + path - The directory path
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function mkdir(string path) returns Error?;
  • rmdir() can be used to remove an empty directory in the server.
# Deletes an empty directory in FTP server.
# ```ballerina
# ftp:Error? response = caller->rmdir(path);
# ```
#
# + path - The directory path
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function rmdir(string path) returns Error?;
  • To delete a file, delete() can be used.
# Deletes a file from FTP server.
# ```ballerina
# ftp:Error? response = caller->delete(path);
# ```
#
# + path - The resource path
# + return -  `()` or else an `ftp:Error` if failed to establish
#             the communication with the FTP server
remote isolated function delete(string path) returns Error?;
  • To rename a file or move it to another directory, rename() can be used.
# Renames a file or moves it to a new location within the same FTP server.
# ```ballerina
# ftp:Error? response = caller->rename(origin, destination);
# ```
#
# + origin - The source file location
# + destination - The destination file location
# + return - `()` or else an `ftp:Error` if failed to establish
#            the communication with the FTP server
remote isolated function rename(string origin, string destination) returns Error?;
  • move() can be used to move a file to a different location on the file server.
# Move a file to a different location on the file server.
# ```ballerina
# ftp:Error? response = caller->move(sourcePath, destinationPath);
# ```
#
# + sourcePath - The current file location
# + destinationPath - The new file location
# + return - An error if the operation fails
remote isolated function move(string sourcePath, string destinationPath) returns Error?;
  • copy() can be used to copy a file to a different location on the file server.
# Copy a file to a different location on the file server.
# ```ballerina
# ftp:Error? response = caller->copy(sourcePath, destinationPath);
# ```
#
# + sourcePath - The file to copy
# + destinationPath - Where to create the copy
# + return - An error if the operation fails
remote isolated function copy(string sourcePath, string destinationPath) returns Error?;
  • exists() can be used to check if a file or folder exists on the file server.
# Check if a file or folder exists on the file server.
# ```ballerina
# boolean|ftp:Error response = caller->exists(path);
# ```
#
# + path - File or folder location to check
# + return - True if it exists, false if it doesn't, or an error if the check fails
remote isolated function exists(string path) returns boolean|Error;
  • size() function can be used to get the size of a file.
# Gets the size of a file resource.
# ```ballerina
# int|ftp:Error response = caller->size(path);
# ```
#
# + path - The resource path
# + return - The file size in bytes or an `ftp:Error` if
#            failed to establish the communication with the FTP server
remote isolated function size(string path) returns int|Error;
  • To get the file list in a directory, list() can be used.
# Gets the file name list in a given folder.
# ```ballerina
# ftp:FileInfo[]|ftp:Error response = caller->list(path);
# ```
#
# + path - The direcotry path
# + return - An array of file names or an `ftp:Error` if failed to
#            establish the communication with the FTP server
remote isolated function list(string path) returns FileInfo[]|Error;
  • To check if a resource is a directory, isDirectory() can be used.
# Checks if a given resource is a directory.
# ```ballerina
# boolean|ftp:Error response = caller->isDirectory(path);
# ```
#
# + path - The resource path
# + return - `true` if given resource is a directory or an `ftp:Error` if
#            an error occurred while checking the path
remote isolated function isDirectory(string path) returns boolean|Error;

6. Samples

6.1. Sending Files

import ballerina/ftp;
import ballerina/io;
import ballerina/lang.'string as strings;

ftp:AuthConfiguration authConfig = {
    credentials: {username: "wso2", password: "wso2123"},
    privateKey: {
        path: "resources/keys/sftp.private.key",
        password: "password"
    }
};

ftp:ClientConfiguration sftpClientConfig = {
    protocol: ftp:SFTP,
    host: "ftp.example.com",
    port: 21,
    auth: authConfig,
    userDirIsRoot: true
};

public function main() returns error? {
    ftp:Client clientEp = check new(sftpClientConfig);
    stream<byte[] & readonly, io:Error?> fileStream = check clientEp->get("/server/book.txt");
    check fileStream.forEach(isolated function(byte[] & readonly fileContent) {
        io:println("File content received: " + checkpanic strings:fromBytes(fileContent));
    });
    stream<io:Block, io:Error?> bStream = check io:fileReadBlocksAsStream("/local/logFile.txt", 1024);
    check clientEp->put("/server", bStream);
    check fileStream.close();
}

6.2. Listening to file changes

import ballerina/ftp;
import ballerina/io;

listener ftp:Listener remoteServer = check new({
    protocol: ftp:SFTP,
    host: "ftp.example.com",
    auth: {
        credentials: {
            username: "wso2",
            password: "wso2123"
        },
        privateKey: {
            path: "resources/keys/sftp.private.key",
            password: "password"
        }
    },
    port: 21,
    path: "/home/in",
    pollingInterval: 2,
    fileNamePattern: "(.*).txt",
    userDirIsRoot: true
});

service on remoteServer {

    remote function onFileChange(ftp:Caller caller, ftp:WatchEvent & readonly event) {
        foreach ftp:FileInfo addedFile in event.addedFiles {
            io:println("Added file path: " + addedFile.path);
            check caller->delete(addedFile.path);
        }

        foreach string deletedFile in event.deletedFiles {
            io:println("Deleted file path: " + deletedFile);
        }
    }
}