{"id":28829425,"url":"https://github.com/ballerina-platform/module-ballerina-ftp","last_synced_at":"2026-02-26T10:49:56.395Z","repository":{"id":36960125,"uuid":"106668325","full_name":"ballerina-platform/module-ballerina-ftp","owner":"ballerina-platform","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-20T09:50:59.000Z","size":57138,"stargazers_count":114,"open_issues_count":1,"forks_count":57,"subscribers_count":85,"default_branch":"master","last_synced_at":"2026-02-20T13:44:26.486Z","etag":null,"topics":["ballerina","ftp","hacktoberfest","integration","sftp","wso2"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ballerina-platform.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2017-10-12T08:51:10.000Z","updated_at":"2026-02-16T13:09:19.000Z","dependencies_parsed_at":"2023-10-03T12:45:31.413Z","dependency_job_id":"52715984-2c99-4235-9642-3fc0c07ce895","html_url":"https://github.com/ballerina-platform/module-ballerina-ftp","commit_stats":null,"previous_names":["wso2-ballerina/module-ftp"],"tags_count":68,"template":false,"template_full_name":null,"purl":"pkg:github/ballerina-platform/module-ballerina-ftp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ballerina-platform%2Fmodule-ballerina-ftp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ballerina-platform%2Fmodule-ballerina-ftp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ballerina-platform%2Fmodule-ballerina-ftp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ballerina-platform%2Fmodule-ballerina-ftp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ballerina-platform","download_url":"https://codeload.github.com/ballerina-platform/module-ballerina-ftp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ballerina-platform%2Fmodule-ballerina-ftp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29856752,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T08:51:08.701Z","status":"ssl_error","status_checked_at":"2026-02-26T08:50:19.607Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ballerina","ftp","hacktoberfest","integration","sftp","wso2"],"created_at":"2025-06-19T05:13:32.713Z","updated_at":"2026-02-26T10:49:56.358Z","avatar_url":"https://github.com/ballerina-platform.png","language":"Java","funding_links":[],"categories":["网络编程"],"sub_categories":["Spring Cloud框架"],"readme":"Ballerina FTP Library\n=====================\n\n  [![Build](https://github.com/ballerina-platform/module-ballerina-ftp/actions/workflows/build-timestamped-master.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-ftp/actions/workflows/build-timestamped-master.yml)\n  [![codecov](https://codecov.io/gh/ballerina-platform/module-ballerina-ftp/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerina-ftp)\n  [![Trivy](https://github.com/ballerina-platform/module-ballerina-ftp/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-ftp/actions/workflows/trivy-scan.yml)\n  [![GraalVM Check](https://github.com/ballerina-platform/module-ballerina-ftp/actions/workflows/build-with-bal-test-graalvm.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-ftp/actions/workflows/build-with-bal-test-graalvm.yml)\n  [![GitHub Last Commit](https://img.shields.io/github/last-commit/ballerina-platform/module-ballerina-ftp.svg?label=Last%20Commit)](https://github.com/ballerina-platform/module-ballerina-ftp/commits/master)\n  [![Github issues](https://img.shields.io/github/issues/ballerina-platform/ballerina-standard-library/module/ftp.svg?label=Open%20Issues)](https://github.com/ballerina-platform/ballerina-standard-library/labels/module%2Fftp)\n\nThis module provides an FTP/SFTP client and an FTP/SFTP server listener implementation to facilitate an FTP/SFTP connection connected to a remote location. Additionally, it supports FTPS (FTP over SSL/TLS) to facilitate secure file transfers.\n\n### FTP client\n\nThe `ftp:Client` connects to an FTP server and performs various operations on the files. Currently, it supports the\ngeneric FTP operations; `get`, `delete`, `put`, `append`, `mkdir`, `rmdir`, `isDirectory`, `rename`, `size`, and\n `list`. The client also provides typed data operations for reading and writing files as text, JSON, XML, CSV, and binary data, with streaming support for handling large files efficiently.\n\nAn FTP client is defined using the `protocol` and `host` parameters and optionally, the `port` and\n`auth`. Authentication configuration can be configured using the `auth` parameter for Basic Auth and\nprivate key.\n\nAn authentication-related configuration can be given to the FTP client with the `auth` configuration.\n\n##### Create a client\n\nThe following code creates an FTP client and performs the I/O operations, which connect to the FTP server with Basic Auth.\n```ballerina\n// Define the FTP client configuration.\nftp:ClientConfiguration ftpConfig = {\n    protocol: ftp:FTP,\n    host: \"\u003cThe FTP host\u003e\",\n    port: \u003cThe FTP port\u003e,\n    auth: {\n        credentials: {\n            username: \"\u003cThe FTP username\u003e\",\n            password: \"\u003cThe FTP passowrd\u003e\"\n        }\n    }\n};\n\n// Create the FTP client.\nftp:Client|ftp:Error ftpClient = new(ftpConfig);\n```\n\n##### Create a directory\n\nThe following code creates a directory in the remote FTP server.\n\n```ballerina\nftp:Error? mkdirResponse = ftpClient-\u003emkdir(\"\u003cThe directory path\u003e\");\n```\n\n##### Upload a file to a remote server\n\nThe following code uploads a file to a remote FTP server.\n\n```ballerina\nstream\u003cio:Block, io:Error?\u003e fileByteStream\n    = check io:fileReadBlocksAsStream(putFilePath, \u003cBlock size\u003e);\nftp:Error? putResponse = ftpClient-\u003eput(\"\u003cThe resource path\u003e\", fileByteStream);\n```\n\nYou can also upload files as specific types:\n\nUpload as text:\n```ballerina\nftp:Error? result = ftpClient-\u003eputText(\"\u003cThe file path\u003e\", \"Hello, World!\");\n```\n\nUpload as JSON or record:\n```ballerina\n// Upload JSON\njson jsonData = {name: \"John\", age: 30};\nftp:Error? result = ftpClient-\u003eputJson(\"\u003cThe file path\u003e\", jsonData);\n\n// Upload a record\ntype User record {\n    string name;\n    int age;\n};\n\nUser user = {name: \"Jane\", age: 25};\nftp:Error? result = ftpClient-\u003eputJson(\"\u003cThe file path\u003e\", user);\n```\n\nUpload as XML:\n```ballerina\nxml xmlData = xml `\u003cconfig\u003e\u003cdatabase\u003emydb\u003c/database\u003e\u003c/config\u003e`;\nftp:Error? result = ftpClient-\u003eputXml(\"\u003cThe file path\u003e\", xmlData);\n```\n\nUpload as CSV (string arrays or typed records):\n```ballerina\n// Upload as string array of arrays\nstring[][] csvData = [[\"Name\", \"Age\"], [\"John\", \"30\"], [\"Jane\", \"25\"]];\nftp:Error? result = ftpClient-\u003eputCsv(\"\u003cThe file path\u003e\", csvData);\n\n// Upload records as CSV\ntype Person record {\n    string name;\n    int age;\n};\n\nPerson[] people = [{name: \"John\", age: 30}, {name: \"Jane\", age: 25}];\nftp:Error? result = ftpClient-\u003eputCsv(\"\u003cThe file path\u003e\", people);\n```\n\nUpload as bytes:\n```ballerina\nbyte[] binaryData = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // \"Hello\"\nftp:Error? result = ftpClient-\u003eputBytes(\"\u003cThe file path\u003e\", binaryData);\n```\n\n##### Compress and upload a file to a remote server\n\nThe following code compresses and uploads a file to a remote FTP server.\n\n```ballerina\n// Set the optional boolean flag as 'true' to compress before uploading\nstream\u003cio:Block, io:Error?\u003e fileByteStream\n    = check io:fileReadBlocksAsStream(putFilePath, \u003cBlock size\u003e);\nftp:Error? compressedPutResponse = ftpClient-\u003eput(\"\u003cResource path\u003e\",\n    fileByteStream, compressionType=ZIP);\n```\n\n##### Get the size of a remote file\n\nThe following code gets the size of a file in a remote FTP server.\n\n```ballerina\nint|ftp:Error sizeResponse = ftpClient-\u003esize(\"\u003cThe resource path\u003e\");\n```\n\n##### Read the content of a remote file\n\nThe following code reads the content of a file in a remote FTP server. The FTP client supports various data types including text, JSON, XML, CSV, and binary data through typed get operations.\n\nRead as text:\n```ballerina\nstring fileContent = check ftpClient-\u003egetText(\"\u003cThe file path\u003e\");\n```\n\nRead as JSON or typed record:\n```ballerina\n// Read as JSON\njson jsonData = check ftpClient-\u003egetJson(\"\u003cThe file path\u003e\");\n\n// Read as a specific record type\ntype User record {\n    string name;\n    int age;\n};\n\nUser userData = check ftpClient-\u003egetJson(\"\u003cThe file path\u003e\");\n```\n\nRead as XML or typed record:\n```ballerina\n// Read as XML\nxml xmlData = check ftpClient-\u003egetXml(\"\u003cThe file path\u003e\");\n\n// Read as a specific record type\ntype Config record {\n    string database;\n    int timeout;\n};\n\nConfig config = check ftpClient-\u003egetXml(\"\u003cThe file path\u003e\");\n```\n\nRead as CSV (string arrays or typed records):\n```ballerina\n// Read as string array of arrays\nstring[][] csvData = check ftpClient-\u003egetCsv(\"\u003cThe file path\u003e\");\n\n// Read as an array of typed records\ntype CsvRecord record {\n    string id;\n    string name;\n    string email;\n};\n\nCsvRecord[] records = check ftpClient-\u003egetCsv(\"\u003cThe file path\u003e\");\n```\n\nRead as bytes:\n```ballerina\n// Read entire file as byte array\nbyte[] fileBytes = check ftpClient-\u003egetBytes(\"\u003cThe file path\u003e\");\n\n// Read as streaming bytes\nstream\u003cbyte[], io:Error?\u003e byteStream = check ftpClient-\u003egetBytesAsStream(\"\u003cThe file path\u003e\");\nrecord {|byte[] value;|} nextBytes = check byteStream.next();\ncheck byteStream.close();\n```\n\n##### Rename/move a remote file\n\nThe following code renames or moves a file to another location in the same remote FTP server.\n\n```ballerina\nftp:Error? renameResponse = ftpClient-\u003erename(\"\u003cThe source file path\u003e\",\n    \"\u003cThe destination file path\u003e\");\n```\n\n##### Delete a remote file\n\nThe following code deletes a remote file in a remote FTP server.\n\n```ballerina\nftp:Error? deleteResponse = ftpClient-\u003edelete(\"\u003cThe resource path\u003e\");\n```\n\n##### Remove a directory from a remote server\n\nThe following code removes a directory in a remote FTP server.\n\n```ballerina\nftp:Error? rmdirResponse = ftpClient-\u003ermdir(\"\u003cThe directory path\u003e\");\n```\n\n### FTP listener\n\nThe `ftp:Listener` is used to listen to a remote FTP location and trigger events when new files are added to or deleted from the directory. The listener supports both a generic `onFileChange` handler for file system events and format-specific content handlers (`onFileText`, `onFileJson`, `onFileXml`, `onFileCsv`, `onFile`) that automatically deserialize file content based on the file type.\n\nAn FTP listener is defined using the mandatory `protocol`, `host`, and  `path` parameters. The authentication\nconfiguration can be done using the `auth` parameter and the polling interval can be configured using the `pollingInterval` parameter.\nThe default polling interval is 60 seconds.\n\nThe `fileNamePattern` parameter can be used to define the type of files the FTP listener will listen to.\nFor instance, if the listener gets invoked for text files, the value `(.*).txt` can be given for the config.\n\nAn authentication-related configuration can be given to the FTP listener with the `auth` configuration.\n\n##### Create a listener\n\nThe FTP Listener can be used to listen to a remote directory. It will keep listening to the specified directory and\nnotify on file addition and deletion periodically.\n\nThe FTP listener supports content handler methods that automatically deserialize file content based on the file type. The listener supports text, JSON, XML, CSV, and binary data types with automatic extension-based routing.\n\nHandle text files:\n```ballerina\nservice on remoteServer {\n    remote function onFileText(string content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"Text file: \" + fileInfo.path);\n        log:print(\"Content: \" + content);\n    }\n}\n```\n\nHandle JSON files (as generic JSON or typed record):\n```ballerina\ntype User record {\n    string name;\n    int age;\n    string email;\n};\n\nservice on remoteServer {\n    // Handle as typed record\n    remote function onFileJson(User content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"User file: \" + fileInfo.path);\n        log:print(\"User name: \" + content.name);\n    }\n}\n```\n\nHandle XML files (as generic XML or typed record):\n```ballerina\ntype Config record {\n    string database;\n    int timeout;\n    boolean debug;\n};\n\nservice on remoteServer {\n    // Handle as typed record\n    remote function onFileXml(Config content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"Config file: \" + fileInfo.path);\n        log:print(\"Database: \" + content.database);\n    }\n}\n```\n\nHandle CSV files (as string arrays or typed record arrays):\n```ballerina\ntype CsvRecord record {\n    string id;\n    string name;\n    string email;\n};\n\nservice on remoteServer {\n    // Handle as array of typed records\n    remote function onFileCsv(CsvRecord[] content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"CSV file: \" + fileInfo.path);\n        foreach CsvRecord record in content {\n            log:print(\"Record: \" + record.id + \", \" + record.name);\n        }\n    }\n}\n```\n\nHandle binary files:\n```ballerina\nservice on remoteServer {\n    // Handle as byte array\n    remote function onFile(byte[] content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"Binary file: \" + fileInfo.path);\n        log:print(\"File size: \" + content.length().toString());\n    }\n}\n```\n\nStream large files:\n```ballerina\nservice on remoteServer {\n    // Stream binary content\n    remote function onFile(stream\u003cbyte[], error?\u003e content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"Streaming file: \" + fileInfo.path);\n        record {|byte[] value;|} nextBytes = check content.next();\n        while nextBytes is record {|byte[] value;|} {\n            log:print(\"Received chunk: \" + nextBytes.value.length().toString() + \" bytes\");\n            nextBytes = content.next();\n        }\n        check content.close();\n    }\n}\n```\n\nStream CSV data as typed records:\n```ballerina\ntype DataRow record {\n    string timestamp;\n    string value;\n};\n\nservice on remoteServer {\n    // Stream CSV as records\n    remote function onFileCsv(stream\u003cDataRow, error?\u003e content, ftp:FileInfo fileInfo) returns error? {\n        log:print(\"Streaming CSV file: \" + fileInfo.path);\n        record {|DataRow value;|}|error? nextRow = content.next();\n        while nextRow is record {|DataRow value;|} {\n            log:print(\"Row: \" + nextRow.value.timestamp + \" = \" + nextRow.value.value);\n            nextRow = content.next();\n        }\n        check content.close();\n    }\n}\n```\n\nThe FTP listener automatically routes files to the appropriate content handler based on file extension: `.txt` → `onFileText()`, `.json` → `onFileJson()`, `.xml` → `onFileXml()`, `.csv` → `onFileCsv()`, and other extensions → `onFile()` (fallback handler). You can override the default routing using the `@ftp:FunctionConfig` annotation to specify a custom file name pattern for each handler method.\n\n### Secure access with FTPS\n\nFTPS provides security by encrypting the control and data channels using SSL/TLS. This module supports both Explicit (starts as regular FTP and upgrades via AUTH TLS) and Implicit (SSL/TLS established immediately) modes.\n\n##### FTPS client configuration\n\nTo use FTPS, set the protocol to FTPS and provide the secureSocket configuration including your keystore and truststore.\n\n```ballerina\nftp:ClientConfiguration ftpsConfig = {\n    protocol: ftp:FTPS,\n    host: \"ftps.example.com\",\n    port: 21, // 21 for EXPLICIT, 990 for IMPLICIT\n    auth: {\n        credentials: { username: \"user\", password: \"password\" },\n        secureSocket: {\n            key: {\n                path: \"/path/to/keystore.p12\",\n                password: \"keystore-password\"\n            },\n            cert: {\n                path: \"/path/to/truststore.p12\",\n                password: \"truststore-password\"\n            },\n            mode: ftp:EXPLICIT, // or ftp:IMPLICIT\n            dataChannelProtection: ftp:PRIVATE // PROT P (Encrypted data channel)\n        }\n    }\n};\n\nftp:Client ftpsClient = check new(ftpsConfig);\n```\n\n##### FTPS listener configuration\n\nThe listener can monitor an FTPS-enabled directory and trigger service remote functions.\n\n```ballerina\nlistener ftp:Listener ftpsListener = check new({\n    protocol: ftp:FTPS,\n    host: \"ftps.example.com\",\n    port: 990,\n    path: \"/upload\",\n    pollingInterval: 5,\n    auth: {\n        credentials: { username: \"user\", password: \"password\" },\n        secureSocket: {\n            cert: { path: \"/path/to/truststore.jks\", password: \"password\" },\n            mode: ftp:IMPLICIT\n        }\n    }\n});\n```\n\n##### CSV fail-safe mode\n\nThe FTP listener supports a fail-safe mode for CSV file processing When enabled, malformed CSV records are automatically skipped and written to a separate error log file in the current directory (`\u003cfilename\u003e_error.log`), allowing the processing to continue without interruption.\n\nThis is only supported in `onFileCsv` trigger methods in ftp listener.\n\n**Listener Configuration**:\n\n```ballerina\nlistener ftp:Listener remoteServer = check new({\n    protocol: ftp:FTP,\n    host: \"\u003cThe FTP host\u003e\",\n    port: \u003cThe FTP port\u003e,\n    path: \"\u003cThe remote FTP directory location\u003e\",\n    pollingInterval: \u003cPolling interval\u003e,\n    auth: {\n        credentials: {username: \"\u003cThe FTP username\u003e\", password: \"\u003cThe FTP password\u003e\"}\n    },\n    csvFailSafe: {\n        contentType: ftp:RAW_AND_METADATA\n    }\n});\n```\n\nThe `contentType` field in `csvFailSafe` specifies what information to log when a malformed CSV record is encountered.\n\n- `METADATA` (default) - Logs only metadata about the error (location, error message)\n- `RAW` - Logs the raw content of the malformed record (offending row)\n- `RAW_AND_METADATA` - Logs both the raw content and metadata for comprehensive debugging\n\n### Secure access with SFTP\n\nSFTP is a secure protocol alternative to the FTP, which runs on top of the SSH protocol.\nThere are several ways to authenticate an SFTP server. One is using the username and the password.\nAnother way is using the client's private key. The Ballerina SFTP client and the listener support only those authentication standards.\nAn authentication-related configuration can be given to the SFTP client/listener with the `auth` configuration.\nPassword-based authentication is defined with the `credentials` configuration while the private key based authentication is defined with the `privateKey` configuration.\n\n#### SFTP client configuration\n\n```ballerina\nftp:ClientConfiguration sftpConfig = {\n    protocol: ftp:SFTP,\n    host: \"\u003cThe SFTP host\u003e\",\n    port: \u003cThe SFTP port\u003e,\n    auth: {\n        credentials: {username: \"\u003cThe SFTP username\u003e\", password: \"\u003cThe SFTP password\u003e\"},\n        privateKey: {\n            path: \"\u003cThe private key file path\u003e\",\n            password: \"\u003cThe private key file password\u003e\"\n        }\n    }\n};\n```\n\n#### SFTP listener configuration\n\n```ballerina\nlistener ftp:Listener remoteServer = check new({\n    protocol: ftp:SFTP,\n    host: \"\u003cThe SFTP host\u003e\",\n    port: \u003cThe SFTP port\u003e,\n    path: \"\u003cThe remote SFTP directory location\u003e\",\n    pollingInterval: \u003cPolling interval\u003e,\n    fileNamePattern: \"\u003cFile name pattern\u003e\",\n    auth: {\n        credentials: {username: \"\u003cThe SFTP username\u003e\", password: \"\u003cThe SFTP password\u003e\"},\n        privateKey: {\n            path: \"\u003cThe private key file path\u003e\",\n            password: \"\u003cThe private key file password\u003e\"\n        }\n    }\n});\n```\n\n## Issues and projects \n\nIssues and Projects tabs are disabled for this repository as this is part of the Ballerina Standard Library. To report bugs, request new features, start new discussions, view project boards, etc. please visit Ballerina Standard Library [parent repository](https://github.com/ballerina-platform/ballerina-standard-library). \n\nThis repository only contains the source code for the library.\n\n## Build from the source\n\n### Set up the prerequisites\n\n1. Download and install Java SE Development Kit (JDK) version 21 (from one of the following locations).\n\n   * [Oracle](https://www.oracle.com/java/technologies/downloads/)\n\n   * [OpenJDK](https://adoptium.net/)\n\n        \u003e **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK.\n     \n### Build the source\n\nExecute the commands below to build from source.\n\n1. To build the library:\n   ```    \n   ./gradlew clean build\n   ```\n\n2. To run the tests:\n   ```\n   ./gradlew clean test\n   ```\n\n3. To run a group of tests\n   ```\n   ./gradlew clean test -Pgroups=\u003ctest_group_names\u003e\n   ```\n\n4. To build the library without the tests:\n   ```\n   ./gradlew clean build -x test\n   ```\n\n5. To debug library implementation:\n   ```\n   ./gradlew clean build -Pdebug=\u003cport\u003e\n   ```\n\n6. To debug with Ballerina language:\n   ```\n   ./gradlew clean build -PbalJavaDebug=\u003cport\u003e\n   ```\n\n7. Publish the generated artifacts to the local Ballerina central repository:\n    ```\n    ./gradlew clean build -PpublishToLocalCentral=true\n    ```\n\n8. Publish the generated artifacts to the Ballerina central repository:\n   ```\n   ./gradlew clean build -PpublishToCentral=true\n   ```\n\n## Contribute to Ballerina\n\nAs an open source project, Ballerina welcomes contributions from the community. \n\nFor more information, go to the [contribution guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md).\n\n## Code of conduct\n\nAll contributors are encouraged to read the [Ballerina Code of Conduct](https://ballerina.io/code-of-conduct).\n\n## Useful links\n\n* For more information go to the [`ftp` library](https://lib.ballerina.io/ballerina/ftp/latest).\n* For example demonstrations of the usage, go to [Ballerina By Examples](https://ballerina.io/learn/by-example/).\n* Chat live with us via our [Discord server](https://discord.gg/ballerinalang).\n* Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fballerina-platform%2Fmodule-ballerina-ftp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fballerina-platform%2Fmodule-ballerina-ftp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fballerina-platform%2Fmodule-ballerina-ftp/lists"}