├── README.md ├── Salesforce ├── aura │ ├── FTP_DeleteFiles │ │ ├── FTP_DeleteFiles.cmp │ │ ├── FTP_DeleteFiles.cmp-meta.xml │ │ ├── FTP_DeleteFiles.css │ │ ├── FTP_DeleteFilesController.js │ │ └── FTP_DeleteFilesHelper.js │ ├── FTP_DownloadAllFiles │ │ ├── FTP_DownloadAllFiles.cmp │ │ ├── FTP_DownloadAllFiles.cmp-meta.xml │ │ ├── FTP_DownloadAllFiles.css │ │ ├── FTP_DownloadAllFilesController.js │ │ └── FTP_DownloadAllFilesHelper.js │ ├── FTP_DownloadSpecifiedFiles │ │ ├── FTP_DownloadSpecifiedFiles.cmp │ │ ├── FTP_DownloadSpecifiedFiles.cmp-meta.xml │ │ ├── FTP_DownloadSpecifiedFiles.css │ │ ├── FTP_DownloadSpecifiedFilesController.js │ │ └── FTP_DownloadSpecifiedFilesHelper.js │ ├── FTP_TransferFiles │ │ ├── FTP_TransferFiles.cmp │ │ ├── FTP_TransferFiles.cmp-meta.xml │ │ ├── FTP_TransferFiles.css │ │ ├── FTP_TransferFilesController.js │ │ └── FTP_TransferFilesHelper.js │ └── FTP_UploadFile │ │ ├── FTP_UploadFile.cmp │ │ ├── FTP_UploadFile.cmp-meta.xml │ │ ├── FTP_UploadFile.css │ │ ├── FTP_UploadFileController.js │ │ └── FTP_UploadFileHelper.js ├── classes │ ├── FTPFileResponseWrapper.cls │ ├── FTPFileResponseWrapper.cls-meta.xml │ ├── FTPResponseWrapper.cls │ ├── FTPResponseWrapper.cls-meta.xml │ ├── FTPWebService.cls │ ├── FTPWebService.cls-meta.xml │ ├── FTPWebServiceCompCtrl.cls │ ├── FTPWebServiceCompCtrl.cls-meta.xml │ ├── FTPWebServiceExample.cls │ ├── FTPWebServiceExample.cls-meta.xml │ ├── FTPWebServiceUtility.cls │ ├── FTPWebServiceUtility.cls-meta.xml │ ├── FileTransferWrapper.cls │ ├── FileTransferWrapper.cls-meta.xml │ ├── FileWrapper.cls │ └── FileWrapper.cls-meta.xml ├── objects │ └── FTP_Web_Service_Configuration__c.object ├── package.xml └── quickActions │ ├── Account.FTP_Delete_Files.quickAction │ ├── Account.FTP_Download_All_Files.quickAction │ ├── Account.FTP_Download_Specified_Files.quickAction │ ├── Account.FTP_Transfer_File_One_FTP_to_Other_FTP.quickAction │ └── Account.FTP_Upload_File.quickAction └── images ├── delete.png ├── download.png ├── transfer.png └── upload.png /README.md: -------------------------------------------------------------------------------- 1 | # [FTP API](https://www.ftp-api.com/) 2 | 3 | In this tutorial, we will see how we can directly communicate with FTP / SFTP server using REST API. It solve's problem like using FTP / SFTP server from salesforce or other platforms using http callouts. 4 | 5 | ## Introduction 6 | 7 | FTP API is designed for people who need to perform actions like DOWNLOAD, UPLOAD, DELETE and TRANSFER on the FTP / SFTP server using REST API's. In these API's lets you control nearly all aspects of your FTP / SFTP server operations programatically. These API's are based on GET, POST and DELETE operations. 8 | 9 | ### How to Use FTP API's 10 | 11 | * UPLOAD: 12 | 13 | This API allow us to upload files on FTP / SFTP Server. User can upload single or multiple files in a single API call. File object contains three parameters : path , filename and body. Body should be provided in Base64 encoding. 14 | 15 | ``` 16 | HTTP Request 17 | POST https://www.ftp-api.com/ftp/upload 18 | ``` 19 | 20 | ![alt text](https://github.com/ftprestapi/ftp-api-example/raw/master/images/upload.png "Upload") 21 | 22 | * DELETE: 23 | 24 | This API allow to delete files on FTP / SFTP server. Specify the files to be deleted, by passing file-name and it's path on FTP / SFTP server. 25 | 26 | ``` 27 | HTTP Request 28 | DELETE https://www.ftp-api.com/ftp/delete 29 | ``` 30 | 31 | ![alt text](https://github.com/ftprestapi/ftp-api-example/raw/master/images/delete.png "Delete") 32 | 33 | * DOWNLOAD: 34 | 35 | This API allow to download files from the FTP / SFTP server. This is a GET API. This operation can be performed in two ways, depending on the number of files you needed. 36 | 37 | First, the user can get all the files from the FTP / SFTP server. 38 | 39 | Second, the user can specify the files which are needed, so the API will return only the specified files. This API is further enhanced based on single or multiple files. 40 | 41 | ``` 42 | HTTP Request 43 | GET https://www.ftp-api.com/ftp/download 44 | ``` 45 | 46 | ![alt text](https://github.com/ftprestapi/ftp-api-example/raw/master/images/download.png "Download") 47 | 48 | * TRANSFER: 49 | 50 | This API allows user to transfer multiple files from one FTP / SFTP server to another FTP / SFTP server without downloading files. 51 | 52 | Note: This API requires both source and destination FTP / SFTP server authentication. 53 | 54 | ``` 55 | HTTP Request 56 | POST https://www.ftp-api.com/ftp/transfer 57 | ``` 58 | 59 | ![alt text](https://github.com/ftprestapi/ftp-api-example/raw/master/images/transfer.png "Transfer") 60 | 61 | 62 | And if you enjoyed this post, share it. 63 | 64 | -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DeleteFiles/FTP_DeleteFiles.cmp: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 41 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 63 | 66 | 69 | 70 | 71 | 72 |
33 |
File Name
34 |
36 |
Path
37 |
39 |
Message
40 |
42 |
Status
43 |
45 |
Code
46 |
53 |
54 | {!oFileWrapper.FileName} 55 |
56 |
58 |
{!oFileWrapper.Path}
59 |
61 |
{!oFileWrapper.Message}
62 |
64 |
{!oFileWrapper.Status}
65 |
67 |
{!oFileWrapper.Code}
68 |
73 |
74 |
75 | 76 |
-------------------------------------------------------------------------------- /Salesforce/aura/FTP_DeleteFiles/FTP_DeleteFiles.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | A Lightning Component Bundle 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DeleteFiles/FTP_DeleteFiles.css: -------------------------------------------------------------------------------- 1 | .THIS .has-error{ 2 | background-color: #c23934 !Important; 3 | color: #ffffff !Important; 4 | } -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DeleteFiles/FTP_DeleteFilesController.js: -------------------------------------------------------------------------------- 1 | ({ 2 | doInit : function(component, event, helper) { 3 | helper.deleteFiles(component, event); 4 | } 5 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DeleteFiles/FTP_DeleteFilesHelper.js: -------------------------------------------------------------------------------- 1 | ({ 2 | deleteFiles : function(component, event) { 3 | this.startWaiting(component); 4 | var action = component.get("c.deleteFilesFromFtpServer"); 5 | action.setParams({ recordId : component.get("v.recordId") }); 6 | action.setCallback(this, function(response) { 7 | var state = response.getState(); 8 | if (state === "SUCCESS") { 9 | let result = response.getReturnValue(); 10 | component.set("v.oFTPResponseWrapper", result); 11 | //this.showToastMessage(component, event, 'Success', "File Deleted Successfully!", 'success'); 12 | }else if (state === "ERROR") { 13 | var errors = response.getError(); 14 | if (errors) { 15 | if (errors[0] && errors[0].message) { 16 | this.showToastMessage(component, event, 'Error', errors[0].message, 'error'); 17 | console.log("Error message: " + errors[0].message); 18 | } 19 | } else { 20 | this.showToastMessage(component, event, 'Error', "Unknown error", 'error'); 21 | console.log("Unknown error"); 22 | } 23 | } 24 | this.stopWaiting(component); 25 | }); 26 | $A.enqueueAction(action); 27 | }, 28 | 29 | showToastMessage : function(component, event, title, message, type) { 30 | var toastEvent = $A.get("e.force:showToast"); 31 | toastEvent.setParams({ 32 | "title": title, 33 | "message": message, 34 | "type":type, 35 | "duration":10000 36 | }); 37 | toastEvent.fire(); 38 | }, 39 | 40 | startWaiting : function(component) { 41 | component.set("v.showSpinner", true); 42 | }, 43 | 44 | stopWaiting : function(component){ 45 | component.set("v.showSpinner", false); 46 | } 47 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadAllFiles/FTP_DownloadAllFiles.cmp: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 41 | 44 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 66 | 69 | 72 | 75 | 76 | 77 | 78 |
33 |
File Name
34 |
36 |
Path
37 |
39 |
LastModifiedDateTime (milliseconds)
40 |
42 |
Message
43 |
45 |
Status
46 |
48 |
Code
49 |
56 |
57 | {!oFileWrapper.FileName} 58 |
59 |
61 |
{!oFileWrapper.Path}
62 |
64 |
{!oFileWrapper.LastModifiedDateTime}
65 |
67 |
{!oFileWrapper.Message}
68 |
70 |
{!oFileWrapper.Status}
71 |
73 |
{!oFileWrapper.Code}
74 |
79 |
80 |
81 | 82 |
-------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadAllFiles/FTP_DownloadAllFiles.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | A Lightning Component Bundle 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadAllFiles/FTP_DownloadAllFiles.css: -------------------------------------------------------------------------------- 1 | .THIS .has-error{ 2 | background-color: #c23934 !Important; 3 | color: #ffffff !Important; 4 | } -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadAllFiles/FTP_DownloadAllFilesController.js: -------------------------------------------------------------------------------- 1 | ({ 2 | doInit : function(component, event, helper) { 3 | helper.downloadFile(component, event); 4 | } 5 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadAllFiles/FTP_DownloadAllFilesHelper.js: -------------------------------------------------------------------------------- 1 | ({ 2 | downloadFile : function(component, event) { 3 | this.startWaiting(component); 4 | var action = component.get("c.downloadAllFilesFromFTPServer"); 5 | action.setParams({ recordId : component.get("v.recordId") }); 6 | action.setCallback(this, function(response) { 7 | var state = response.getState(); 8 | if (state === "SUCCESS") { 9 | let result = response.getReturnValue(); 10 | component.set("v.oFTPResponseWrapper", result); 11 | //this.showToastMessage(component, event, 'Success', "File Download Successfully!", 'success'); 12 | }else if (state === "ERROR") { 13 | var errors = response.getError(); 14 | if (errors) { 15 | if (errors[0] && errors[0].message) { 16 | this.showToastMessage(component, event, 'Error', errors[0].message, 'error'); 17 | console.log("Error message: " + errors[0].message); 18 | } 19 | } else { 20 | this.showToastMessage(component, event, 'Error', "Unknown error", 'error'); 21 | console.log("Unknown error"); 22 | } 23 | } 24 | this.stopWaiting(component); 25 | }); 26 | $A.enqueueAction(action); 27 | }, 28 | 29 | showToastMessage : function(component, event, title, message, type) { 30 | var toastEvent = $A.get("e.force:showToast"); 31 | toastEvent.setParams({ 32 | "title": title, 33 | "message": message, 34 | "type":type, 35 | "duration":10000 36 | }); 37 | toastEvent.fire(); 38 | }, 39 | 40 | startWaiting : function(component) { 41 | component.set("v.showSpinner", true); 42 | }, 43 | 44 | stopWaiting : function(component){ 45 | component.set("v.showSpinner", false); 46 | } 47 | 48 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadSpecifiedFiles/FTP_DownloadSpecifiedFiles.cmp: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 41 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 63 | 66 | 69 | 70 | 71 | 72 |
33 |
File Name
34 |
36 |
Path
37 |
39 |
Message
40 |
42 |
Status
43 |
45 |
Code
46 |
53 |
54 | {!oFileWrapper.FileName} 55 |
56 |
58 |
{!oFileWrapper.Path}
59 |
61 |
{!oFileWrapper.Message}
62 |
64 |
{!oFileWrapper.Status}
65 |
67 |
{!oFileWrapper.Code}
68 |
73 |
74 |
75 | 76 |
-------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadSpecifiedFiles/FTP_DownloadSpecifiedFiles.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | A Lightning Component Bundle 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadSpecifiedFiles/FTP_DownloadSpecifiedFiles.css: -------------------------------------------------------------------------------- 1 | .THIS .has-error{ 2 | background-color: #c23934 !Important; 3 | color: #ffffff !Important; 4 | } -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadSpecifiedFiles/FTP_DownloadSpecifiedFilesController.js: -------------------------------------------------------------------------------- 1 | ({ 2 | doInit : function(component, event, helper) { 3 | helper.downloadFile(component, event); 4 | } 5 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_DownloadSpecifiedFiles/FTP_DownloadSpecifiedFilesHelper.js: -------------------------------------------------------------------------------- 1 | ({ 2 | downloadFile : function(component, event) { 3 | this.startWaiting(component); 4 | var action = component.get("c.downloadSelectedFilesFromFtpServer"); 5 | action.setParams({ recordId : component.get("v.recordId") }); 6 | action.setCallback(this, function(response) { 7 | var state = response.getState(); 8 | if (state === "SUCCESS") { 9 | let result = response.getReturnValue(); 10 | component.set("v.oFTPResponseWrapper", result); 11 | //this.showToastMessage(component, event, 'Success', "File Download Successfully!", 'success'); 12 | }else if (state === "ERROR") { 13 | var errors = response.getError(); 14 | if (errors) { 15 | if (errors[0] && errors[0].message) { 16 | this.showToastMessage(component, event, 'Error', errors[0].message, 'error'); 17 | console.log("Error message: " + errors[0].message); 18 | } 19 | } else { 20 | this.showToastMessage(component, event, 'Error', "Unknown error", 'error'); 21 | console.log("Unknown error"); 22 | } 23 | } 24 | this.stopWaiting(component); 25 | }); 26 | $A.enqueueAction(action); 27 | }, 28 | 29 | showToastMessage : function(component, event, title, message, type) { 30 | var toastEvent = $A.get("e.force:showToast"); 31 | toastEvent.setParams({ 32 | "title": title, 33 | "message": message, 34 | "type":type, 35 | "duration":10000 36 | }); 37 | toastEvent.fire(); 38 | }, 39 | 40 | startWaiting : function(component) { 41 | component.set("v.showSpinner", true); 42 | }, 43 | 44 | stopWaiting : function(component){ 45 | component.set("v.showSpinner", false); 46 | } 47 | 48 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_TransferFiles/FTP_TransferFiles.cmp: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 41 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 63 | 66 | 69 | 70 | 71 | 72 |
33 |
File Name
34 |
36 |
Path
37 |
39 |
Message
40 |
42 |
Status
43 |
45 |
Code
46 |
53 |
54 | {!oFileWrapper.FileName} 55 |
56 |
58 |
{!oFileWrapper.Path}
59 |
61 |
{!oFileWrapper.Message}
62 |
64 |
{!oFileWrapper.Status}
65 |
67 |
{!oFileWrapper.Code}
68 |
73 |
74 |
75 |
-------------------------------------------------------------------------------- /Salesforce/aura/FTP_TransferFiles/FTP_TransferFiles.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | A Lightning Component Bundle 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/aura/FTP_TransferFiles/FTP_TransferFiles.css: -------------------------------------------------------------------------------- 1 | .THIS .has-error{ 2 | background-color: #c23934 !Important; 3 | color: #ffffff !Important; 4 | } -------------------------------------------------------------------------------- /Salesforce/aura/FTP_TransferFiles/FTP_TransferFilesController.js: -------------------------------------------------------------------------------- 1 | ({ 2 | doInit : function(component, event, helper) { 3 | helper.transferFile(component, event); 4 | } 5 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_TransferFiles/FTP_TransferFilesHelper.js: -------------------------------------------------------------------------------- 1 | ({ 2 | transferFile : function(component, event) { 3 | this.startWaiting(component); 4 | var action = component.get("c.transferFilesFronOneFtpServertoOtherFtpServer"); 5 | action.setCallback(this, function(response) { 6 | var state = response.getState(); 7 | if (state === "SUCCESS") { 8 | let result = response.getReturnValue(); 9 | component.set("v.oFTPResponseWrapper", result); 10 | //this.showToastMessage(component, event, 'Success', "File Transfered Successfully!", 'success'); 11 | }else if (state === "ERROR") { 12 | var errors = response.getError(); 13 | if (errors) { 14 | if (errors[0] && errors[0].message) { 15 | this.showToastMessage(component, event, 'Error', errors[0].message, 'error'); 16 | console.log("Error message: " + errors[0].message); 17 | } 18 | } else { 19 | this.showToastMessage(component, event, 'Error', "Unknown error", 'error'); 20 | console.log("Unknown error"); 21 | } 22 | } 23 | this.stopWaiting(component); 24 | }); 25 | $A.enqueueAction(action); 26 | }, 27 | 28 | showToastMessage : function(component, event, title, message, type) { 29 | var toastEvent = $A.get("e.force:showToast"); 30 | toastEvent.setParams({ 31 | "title": title, 32 | "message": message, 33 | "type":type, 34 | "duration":10000 35 | }); 36 | toastEvent.fire(); 37 | }, 38 | 39 | startWaiting : function(component) { 40 | component.set("v.showSpinner", true); 41 | }, 42 | 43 | stopWaiting : function(component){ 44 | component.set("v.showSpinner", false); 45 | } 46 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_UploadFile/FTP_UploadFile.cmp: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 41 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 63 | 66 | 69 | 70 | 71 | 72 |
33 |
File Name
34 |
36 |
Path
37 |
39 |
Message
40 |
42 |
Status
43 |
45 |
Code
46 |
53 |
54 | {!oFileWrapper.FileName} 55 |
56 |
58 |
{!oFileWrapper.Path}
59 |
61 |
{!oFileWrapper.Message}
62 |
64 |
{!oFileWrapper.Status}
65 |
67 |
{!oFileWrapper.Code}
68 |
73 |
74 |
75 | 76 |
-------------------------------------------------------------------------------- /Salesforce/aura/FTP_UploadFile/FTP_UploadFile.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | A Lightning Component Bundle 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/aura/FTP_UploadFile/FTP_UploadFile.css: -------------------------------------------------------------------------------- 1 | .THIS .has-error{ 2 | background-color: #c23934 !Important; 3 | color: #ffffff !Important; 4 | } -------------------------------------------------------------------------------- /Salesforce/aura/FTP_UploadFile/FTP_UploadFileController.js: -------------------------------------------------------------------------------- 1 | ({ 2 | doInit : function(component, event, helper) { 3 | helper.uploadFile(component, event); 4 | } 5 | }) -------------------------------------------------------------------------------- /Salesforce/aura/FTP_UploadFile/FTP_UploadFileHelper.js: -------------------------------------------------------------------------------- 1 | ({ 2 | uploadFile : function(component, event) { 3 | this.startWaiting(component); 4 | var action = component.get("c.uploadFileToFTPServer"); 5 | action.setParams({ recordId : component.get("v.recordId") }); 6 | action.setCallback(this, function(response) { 7 | var state = response.getState(); 8 | if (state === "SUCCESS") { 9 | let result = response.getReturnValue(); 10 | component.set("v.oFTPResponseWrapper", result); 11 | //this.showToastMessage(component, event, 'Success', "File Uploaded Successfully!", 'success'); 12 | }else if (state === "ERROR") { 13 | var errors = response.getError(); 14 | if (errors) { 15 | if (errors[0] && errors[0].message) { 16 | this.showToastMessage(component, event, 'Error', errors[0].message, 'error'); 17 | console.log("Error message: " + errors[0].message); 18 | } 19 | } else { 20 | this.showToastMessage(component, event, 'Error', "Unknown error", 'error'); 21 | console.log("Unknown error"); 22 | } 23 | } 24 | this.stopWaiting(component); 25 | }); 26 | $A.enqueueAction(action); 27 | }, 28 | 29 | showToastMessage : function(component, event, title, message, type) { 30 | var toastEvent = $A.get("e.force:showToast"); 31 | toastEvent.setParams({ 32 | "title": title, 33 | "message": message, 34 | "type":type, 35 | "duration":10000 36 | }); 37 | toastEvent.fire(); 38 | }, 39 | 40 | startWaiting : function(component) { 41 | component.set("v.showSpinner", true); 42 | }, 43 | 44 | stopWaiting : function(component){ 45 | component.set("v.showSpinner", false); 46 | } 47 | 48 | }) -------------------------------------------------------------------------------- /Salesforce/classes/FTPFileResponseWrapper.cls: -------------------------------------------------------------------------------- 1 | public class FTPFileResponseWrapper { 2 | @AuraEnabled public String FileName; 3 | @AuraEnabled public String Path; 4 | @AuraEnabled public String body; 5 | @AuraEnabled public Long LastModifiedDateTime; 6 | @AuraEnabled public String Status; 7 | @AuraEnabled public String Message; 8 | @AuraEnabled public String Code; 9 | } -------------------------------------------------------------------------------- /Salesforce/classes/FTPFileResponseWrapper.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FTPResponseWrapper.cls: -------------------------------------------------------------------------------- 1 | public class FTPResponseWrapper { 2 | @AuraEnabled public String Status; 3 | @AuraEnabled public String Message; 4 | @AuraEnabled public Integer Code; 5 | @AuraEnabled public List Files; 6 | } -------------------------------------------------------------------------------- /Salesforce/classes/FTPResponseWrapper.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebService.cls: -------------------------------------------------------------------------------- 1 | public class FTPWebService{ 2 | 3 | //Upload Files on FTP Server 4 | public static HttpResponse uploadFileOnFTPServer(String endpoint, String http_method, String http_body){ 5 | HttpRequest req = createHttpRequest(endpoint, http_method, http_body, FTPWebServiceUtility.oSourceFtpWebServiceConfig); 6 | Http httpreq = new Http(); 7 | return httpreq.send(req); 8 | } 9 | 10 | //Reterive Files from FTP Server 11 | public static HttpResponse fileExportFromFTPServer(String endpoint, String http_method, String http_body, Id parentRecordId){ 12 | HttpRequest req = createHttpRequest(endpoint, http_method, http_body, FTPWebServiceUtility.oSourceFtpWebServiceConfig); 13 | Http httpreq = new Http(); 14 | return httpreq.send(req); 15 | } 16 | 17 | //Delete files from ftp server 18 | public static HttpResponse deleteFiles(String endpoint, String http_method, String http_body){ 19 | HttpRequest req = createHttpRequest(endpoint, http_method, http_body, FTPWebServiceUtility.oSourceFtpWebServiceConfig); 20 | Http httpreq = new Http(); 21 | return httpreq.send(req); 22 | } 23 | 24 | //Transfer files from one FTP Server to Other FTP Server 25 | public static HttpResponse transferFiles(String endpoint, String http_method, String http_body){ 26 | HttpRequest req = createFileTransferHttpRequest(endpoint, http_method, http_body, FTPWebServiceUtility.oSourceFtpWebServiceConfig, FTPWebServiceUtility.oTargetFtpWebServiceConfig); 27 | Http httpreq = new Http(); 28 | return httpreq.send(req); 29 | } 30 | 31 | private static HttpRequest createHttpRequest(String endpoint, String method, String body, FTP_Web_Service_Configuration__c oConfiguration){ 32 | HttpRequest req = new HttpRequest(); 33 | req.setHeader('ftp-host', oConfiguration.Host__c); 34 | req.setHeader('ftp-type', oConfiguration.Source__c); 35 | req.setHeader('username', oConfiguration.Username__c); 36 | req.setHeader('password', oConfiguration.Password__c); 37 | req.setHeader('port', oConfiguration.Port__c); 38 | req.setHeader('Content-Type', 'application/json'); 39 | req.setEndpoint(endpoint); 40 | req.setMethod(method); 41 | if(String.isNotBlank(body) && String.isNotEmpty(body)){ 42 | req.setBody(body); 43 | } 44 | req.setTimeout(120000); 45 | return req; 46 | } 47 | 48 | private static HttpRequest createFileTransferHttpRequest(String endpoint, String method, String body, FTP_Web_Service_Configuration__c oSourceConfiguration, FTP_Web_Service_Configuration__c oTargetConfiguration){ 49 | HttpRequest req = new HttpRequest(); 50 | req.setHeader('source-ftp-type', oSourceConfiguration.Source__c); 51 | req.setHeader('source-ftp-host', oSourceConfiguration.Host__c); 52 | req.setHeader('source-username', oSourceConfiguration.Username__c); 53 | req.setHeader('source-password', oSourceConfiguration.Password__c); 54 | req.setHeader('source-port', oSourceConfiguration.Port__c); 55 | req.setHeader('target-ftp-host', oTargetConfiguration.Host__c); 56 | req.setHeader('target-username', oTargetConfiguration.Username__c); 57 | req.setHeader('target-password', oTargetConfiguration.Password__c); 58 | req.setHeader('target-port', oTargetConfiguration.Port__c); 59 | req.setHeader('target-ftp-type', oTargetConfiguration.Source__c); 60 | req.setHeader('Content-Type', 'application/json'); 61 | req.setEndpoint(endpoint); 62 | req.setMethod(method); 63 | if(String.isNotBlank(body) && String.isNotEmpty(body)){ 64 | req.setBody(body); 65 | } 66 | req.setTimeout(120000); 67 | return req; 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebService.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebServiceCompCtrl.cls: -------------------------------------------------------------------------------- 1 | public class FTPWebServiceCompCtrl { 2 | 3 | @AuraEnabled 4 | public static FTPResponseWrapper uploadFileToFTPServer(Id recordId){ 5 | try{ 6 | return FTPWebServiceExample.uploadFiles(recordId); 7 | }catch(Exception ex){ 8 | throw new AuraException(ex.getMessage()); 9 | } 10 | } 11 | 12 | @AuraEnabled 13 | public static FTPResponseWrapper downloadAllFilesFromFTPServer(Id recordId){ 14 | try{ 15 | return FTPWebServiceExample.downloadAllFiles(recordId); 16 | }catch(DMLException dmlex){ 17 | system.debug('==Exception=='+dmlex.getDMLMessage(0)+dmlex.getStackTraceString()); 18 | throw new AuraException(dmlex.getDMLMessage(0)); 19 | }catch(Exception ex){ 20 | system.debug('==Exception=='+ex.getMessage()+ex.getStackTraceString()); 21 | throw new AuraException(ex.getMessage()); 22 | } 23 | } 24 | 25 | @AuraEnabled 26 | public static FTPResponseWrapper downloadSelectedFilesFromFtpServer(Id recordId){ 27 | try{ 28 | return FTPWebServiceExample.downloadSelectedFilesOnly(recordId); 29 | }catch(DMLException dmlex){ 30 | throw new AuraException(dmlex.getDMLMessage(0)); 31 | }catch(Exception ex){ 32 | throw new AuraException(ex.getMessage()); 33 | } 34 | } 35 | 36 | @AuraEnabled 37 | public static FTPResponseWrapper transferFilesFronOneFtpServertoOtherFtpServer(){ 38 | try{ 39 | return FTPWebServiceExample.transferFiles(); 40 | }catch(Exception ex){ 41 | throw new AuraException(ex.getMessage()); 42 | } 43 | } 44 | 45 | @AuraEnabled 46 | public static FTPResponseWrapper deleteFilesFromFtpServer(Id recordId){ 47 | try{ 48 | return FTPWebServiceExample.deleteFiles(recordId); 49 | }catch(Exception ex){ 50 | throw new AuraException(ex.getMessage()+'-'+ex.getStackTraceString()); 51 | } 52 | } 53 | 54 | 55 | 56 | } -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebServiceCompCtrl.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebServiceExample.cls: -------------------------------------------------------------------------------- 1 | public class FTPWebServiceExample { 2 | 3 | public static FTPResponseWrapper uploadFiles(Id parentId){ 4 | List lstFileWrapper = new List(); 5 | for(Attachment oAttachment : [SELECT Id, body, Name FROM Attachment Where ParentId=:parentId]){ 6 | lstFileWrapper.add(new FileWrapper(oAttachment.Name, '/tmp', EncodingUtil.base64Encode(oAttachment.body))); 7 | } 8 | String endpoint = FTPWebServiceUtility.FTP_WebServiceEndpoint+'upload'; 9 | HttpResponse response = FTPWebService.uploadFileOnFTPServer(endpoint, 'POST', JSON.serialize(lstFileWrapper)); 10 | if(response != null){ 11 | if(response.getStatusCode() == 200){ 12 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 13 | }else if(response.getStatusCode() == 400){ 14 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 15 | }else{ 16 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', response.getStatusCode()); 17 | } 18 | }else{ 19 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', 0); 20 | } 21 | } 22 | 23 | public static FTPResponseWrapper downloadAllFiles(Id parentId){ 24 | String endpoint = FTPWebServiceUtility.FTP_WebServiceEndpoint+'download'; 25 | HttpResponse response = FTPWebService.fileExportFromFTPServer(endpoint, 'GET', null, parentId); 26 | if(response != null){ 27 | system.debug('==response=='+response.getBody()); 28 | if(response.getStatusCode() == 200){ 29 | FTPResponseWrapper oFTPResponseWrapper = (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 30 | List lstAttachment = FTPWebServiceUtility.createAttachments(oFTPResponseWrapper.Files, parentId); 31 | if(lstAttachment.size()>0){ insert lstAttachment;} 32 | return oFTPResponseWrapper; 33 | }else if(response.getStatusCode() == 400){ 34 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 35 | }else{ 36 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', response.getStatusCode()); 37 | } 38 | }else{ 39 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', 0); 40 | } 41 | } 42 | 43 | public static FTPResponseWrapper downloadSelectedFilesOnly(Id parentId){ 44 | List lstFileWrapper = new List(); 45 | //lstFileWrapper.add(new FileWrapper(File Name, Path, '')); 46 | lstFileWrapper.add(new FileWrapper('Test.csv','/tmp', null)); 47 | lstFileWrapper.add(new FileWrapper('Test.xml','/tmp', null)); 48 | lstFileWrapper.add(new FileWrapper('Test.xlsx','/tmp', null)); 49 | lstFileWrapper.add(new FileWrapper('Test.pdf','/tmp', null)); 50 | lstFileWrapper.add(new FileWrapper('Test.jpg','/tmp', null)); 51 | 52 | String endpoint = FTPWebServiceUtility.FTP_WebServiceEndpoint+'download'; 53 | HttpResponse response = FTPWebService.fileExportFromFTPServer(endpoint, 'POST', JSON.serialize(lstFileWrapper), parentId); 54 | if(response != null){ 55 | if(response.getStatusCode() == 200){ 56 | FTPResponseWrapper oFTPResponseWrapper = (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 57 | List lstAttachment = FTPWebServiceUtility.createAttachments(oFTPResponseWrapper.Files, parentId); 58 | if(lstAttachment.size()>0){ insert lstAttachment;} 59 | return oFTPResponseWrapper; 60 | }else if(response.getStatusCode() == 400){ 61 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 62 | }else{ 63 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', response.getStatusCode()); 64 | } 65 | }else{ 66 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', 0); 67 | } 68 | } 69 | 70 | public static FTPResponseWrapper transferFiles(){ 71 | List lstFileTransferWrapper = new List(); 72 | //lstFileTransferWrapper.add(new FileWrapper(Source File Name, Source Path, Target File Name, Target Path)); 73 | lstFileTransferWrapper.add(new FileTransferWrapper('Test.csv','/tmp', 'attachmentTest.csv', '/temp1')); 74 | lstFileTransferWrapper.add(new FileTransferWrapper('Test.xml','/tmp','Test.xml', '/temp1')); 75 | lstFileTransferWrapper.add(new FileTransferWrapper('Test.xlsx','/tmp','Test.xlsx', '/temp1')); 76 | lstFileTransferWrapper.add(new FileTransferWrapper('Test.pdf','/tmp', 'Test.pdf', '/temp1')); 77 | lstFileTransferWrapper.add(new FileTransferWrapper('Test.jpg','/tmp', 'Test.jpg', '/temp1')); 78 | 79 | String endpoint = FTPWebServiceUtility.FTP_WebServiceEndpoint+'transfer'; 80 | HttpResponse response = FTPWebService.transferFiles(endpoint, 'POST', JSON.serialize(lstFileTransferWrapper)); 81 | if(response != null){ 82 | if(response.getStatusCode() == 200){ 83 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 84 | }else if(response.getStatusCode() == 400){ 85 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 86 | }else{ 87 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', response.getStatusCode()); 88 | } 89 | }else{ 90 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', 0); 91 | } 92 | } 93 | 94 | public static FTPResponseWrapper deleteFiles(Id ParentId){ 95 | List lstFileWrapper = new List(); 96 | for(Attachment oAttachment : [SELECT Id, Name FROM Attachment Where ParentId=:parentId]){ 97 | lstFileWrapper.add(new FileWrapper(oAttachment.Name, '/tmp', null)); 98 | } 99 | String endpoint = FTPWebServiceUtility.FTP_WebServiceEndpoint+'delete'; 100 | HttpResponse response = FTPWebService.deleteFiles(endpoint, 'DELETE', JSON.serialize(lstFileWrapper)); 101 | if(response != null){ 102 | if(response.getStatusCode() == 200){ 103 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 104 | }else if(response.getStatusCode() == 400){ 105 | return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class); 106 | }else{ 107 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', response.getStatusCode()); 108 | } 109 | }else{ 110 | return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', 0); 111 | } 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebServiceExample.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebServiceUtility.cls: -------------------------------------------------------------------------------- 1 | public class FTPWebServiceUtility { 2 | 3 | public static String FTP_WebServiceEndpoint = 'https://www.ftp-api.com/ftp/'; 4 | 5 | public static FTP_Web_Service_Configuration__c oSourceFtpWebServiceConfig{ 6 | get{ 7 | if(oSourceFtpWebServiceConfig == null){ 8 | return getFTPWebServiceConfiguration(false); 9 | } 10 | return oSourceFtpWebServiceConfig; 11 | } 12 | set; 13 | } 14 | 15 | public static FTP_Web_Service_Configuration__c oTargetFtpWebServiceConfig{ 16 | get{ 17 | if(oTargetFtpWebServiceConfig == null){ 18 | return getFTPWebServiceConfiguration(true); 19 | } 20 | return oTargetFtpWebServiceConfig; 21 | } 22 | set; 23 | } 24 | 25 | public static FTP_Web_Service_Configuration__c getFTPWebServiceConfiguration(Boolean isTargetEnvironment){ 26 | FTP_Web_Service_Configuration__c oWebServiceConfiguration = new FTP_Web_Service_Configuration__c(); 27 | String sSOQL = 'SELECT Id, Host__c, Username__c, Password__c, Port__c, Source__c'; 28 | sSOQL += ' FROM FTP_Web_Service_Configuration__c'; 29 | sSOQL += ' WHERE IsActive__c = true'; 30 | if(isTargetEnvironment){ 31 | sSOQL += ' AND IsTargetEnvironment__c = true'; 32 | }else{ 33 | sSOQL += ' AND IsSourceEnvironment__c = true'; 34 | } 35 | for(FTP_Web_Service_Configuration__c wsc : Database.query(sSOQL)){ 36 | oWebServiceConfiguration = wsc; 37 | } 38 | return oWebServiceConfiguration; 39 | } 40 | 41 | public static List createAttachments(List files, Id parentId){ 42 | List lstAttachment = new List(); 43 | for(FTPFileResponseWrapper oWrapper : files){ 44 | if(oWrapper.Status == 'SUCCESS'){ 45 | lstAttachment.add(createAttachment(oWrapper.fileName, EncodingUtil.base64Decode(oWrapper.body), parentId)); 46 | } 47 | } 48 | return lstAttachment; 49 | } 50 | 51 | public static Attachment createAttachment(String fileName, Blob body, Id parentRecordId){ 52 | Attachment oAttachment = new Attachment(); 53 | oAttachment.Name = fileName; 54 | oAttachment.body = body; 55 | oAttachment.ParentId = parentRecordId; 56 | return oAttachment; 57 | } 58 | 59 | public static FTPResponseWrapper getFTPResponseWrapper(String status, String message, Integer code){ 60 | FTPResponseWrapper oWrapper = new FTPResponseWrapper(); 61 | oWrapper.Status = status; 62 | oWrapper.Message = message; 63 | oWrapper.Code = code; 64 | return oWrapper; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /Salesforce/classes/FTPWebServiceUtility.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FileTransferWrapper.cls: -------------------------------------------------------------------------------- 1 | public class FileTransferWrapper { 2 | public String source_file_Name; 3 | public String source_path; 4 | public String target_file_Name; 5 | public String target_path; 6 | 7 | public FileTransferWrapper(String sourcefileName, String sourcepath, String targetfileName, String targetpath){ 8 | this.source_file_Name = sourcefileName; 9 | this.source_path = sourcepath; 10 | this.target_file_Name = targetfileName; 11 | this.target_path = targetpath; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /Salesforce/classes/FileTransferWrapper.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/classes/FileWrapper.cls: -------------------------------------------------------------------------------- 1 | public class FileWrapper { 2 | public String fileName{get;set;} 3 | public String path{get;set;} 4 | public String body{get;set;} 5 | 6 | public FileWrapper(String fName, String directory, String strBody){ 7 | this.fileName = fName; 8 | this.path = directory; 9 | this.body = strBody; 10 | } 11 | } -------------------------------------------------------------------------------- /Salesforce/classes/FileWrapper.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /Salesforce/objects/FTP_Web_Service_Configuration__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | List 4 | false 5 | 6 | Host__c 7 | false 8 | 9 | 255 10 | false 11 | false 12 | Text 13 | false 14 | 15 | 16 | IsActive__c 17 | false 18 | false 19 | 20 | false 21 | Checkbox 22 | 23 | 24 | IsSourceEnvironment__c 25 | false 26 | false 27 | 28 | false 29 | Checkbox 30 | 31 | 32 | IsTargetEnvironment__c 33 | false 34 | false 35 | 36 | false 37 | Checkbox 38 | 39 | 40 | Password__c 41 | false 42 | 43 | 255 44 | false 45 | false 46 | Text 47 | false 48 | 49 | 50 | Port__c 51 | false 52 | 53 | 255 54 | false 55 | false 56 | Text 57 | false 58 | 59 | 60 | Source__c 61 | false 62 | Source should be FTP or SFTP 63 | 64 | 255 65 | true 66 | false 67 | Text 68 | false 69 | 70 | 71 | Username__c 72 | false 73 | 74 | 255 75 | false 76 | false 77 | Text 78 | false 79 | 80 | 81 | 82 | All 83 | NAME 84 | Host__c 85 | Username__c 86 | Port__c 87 | IsSourceEnvironment__c 88 | IsTargetEnvironment__c 89 | Source__c 90 | IsActive__c 91 | Everything 92 | 93 | 94 | Public 95 | 96 | -------------------------------------------------------------------------------- /Salesforce/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FTPFileResponseWrapper 5 | FTPResponseWrapper 6 | FTPWebService 7 | FTPWebServiceCompCtrl 8 | FTPWebServiceExample 9 | FTPWebServiceUtility 10 | FileTransferWrapper 11 | FileWrapper 12 | ApexClass 13 | 14 | 15 | FTP_DeleteFiles 16 | FTP_DownloadAllFiles 17 | FTP_DownloadSpecifiedFiles 18 | FTP_TransferFiles 19 | FTP_UploadFile 20 | AuraDefinitionBundle 21 | 22 | 23 | FTP_Web_Service_Configuration__c 24 | CustomObject 25 | 26 | 27 | Account.FTP_Delete_Files 28 | Account.FTP_Download_All_Files 29 | Account.FTP_Download_Specified_Files 30 | Account.FTP_Transfer_File_One_FTP_to_Other_FTP 31 | Account.FTP_Upload_File 32 | QuickAction 33 | 34 | 39.0 35 | 36 | -------------------------------------------------------------------------------- /Salesforce/quickActions/Account.FTP_Delete_Files.quickAction: -------------------------------------------------------------------------------- 1 | 2 | 3 | 250 4 | 5 | FTP_DeleteFiles 6 | false 7 | LightningComponent 8 | -100 9 | 10 | -------------------------------------------------------------------------------- /Salesforce/quickActions/Account.FTP_Download_All_Files.quickAction: -------------------------------------------------------------------------------- 1 | 2 | 3 | 250 4 | 5 | FTP_DownloadAllFiles 6 | false 7 | LightningComponent 8 | -100 9 | 10 | -------------------------------------------------------------------------------- /Salesforce/quickActions/Account.FTP_Download_Specified_Files.quickAction: -------------------------------------------------------------------------------- 1 | 2 | 3 | 250 4 | 5 | FTP_DownloadSpecifiedFiles 6 | false 7 | LightningComponent 8 | -100 9 | 10 | -------------------------------------------------------------------------------- /Salesforce/quickActions/Account.FTP_Transfer_File_One_FTP_to_Other_FTP.quickAction: -------------------------------------------------------------------------------- 1 | 2 | 3 | 250 4 | 5 | FTP_TransferFiles 6 | false 7 | LightningComponent 8 | -100 9 | 10 | -------------------------------------------------------------------------------- /Salesforce/quickActions/Account.FTP_Upload_File.quickAction: -------------------------------------------------------------------------------- 1 | 2 | 3 | 250 4 | 5 | FTP_UploadFile 6 | false 7 | LightningComponent 8 | -100 9 | 10 | -------------------------------------------------------------------------------- /images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftprestapi/ftp-api-example/079455289be4ccb254b5b6421f30d8a1ddad3eab/images/delete.png -------------------------------------------------------------------------------- /images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftprestapi/ftp-api-example/079455289be4ccb254b5b6421f30d8a1ddad3eab/images/download.png -------------------------------------------------------------------------------- /images/transfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftprestapi/ftp-api-example/079455289be4ccb254b5b6421f30d8a1ddad3eab/images/transfer.png -------------------------------------------------------------------------------- /images/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftprestapi/ftp-api-example/079455289be4ccb254b5b6421f30d8a1ddad3eab/images/upload.png --------------------------------------------------------------------------------