3

I have requirement wherein a csv file should generate with some users data and need to place the same file in a SFTP path without using any external tool. Can I have an alternative approach to proceed with the same. A sample code would be much appreciated.

  • SFTP stands for SSH File Transfer Protocol? – Vladyslav K Apr 21 '17 at 9:12
  • There is no SFTP support built into the platform so you cannot avoid using an external tool. – Keith C Apr 21 '17 at 9:46
  • yes, In short once the csv file generated in Salesforce system I need to place that file in a different server in a daily basic. (currently I have the username,password &server path, where the file will get placed). – user44228 Apr 21 '17 at 9:51
  • 4
    SFTP requires more advanced logic than is possible in Salesforce's HTTP/Web Services library. Unless your SFTP server has a web interface (e.g. using POST), you're not going to be using Apex Code for this task. It's unlikely that salesforce.com will add (S)FTP services in the system library any time soon. Go vote on this idea. – sfdcfox Apr 21 '17 at 9:53
1

As of V46, there is no direct (native) support for SFDC to SFTP

Options to consider:

  • Use Salesforce's newly-acquired integration platform Mulesoft
  • Use another integration vendor such as Dell Boomi, Skyvia, etc.
  • Sync your sobject(s) to Marketing Cloud and use SFMC's SFTP support

For CSV output, the challenge in SFDC is to not exceed heap when constructing the CSV. As such, leverage the middleware to do the construction of the CSV, perhaps by sending Platform Events like this:

  • CSVStart (contains header row)
  • CSVRowBatch (1..n rows that comfortably fit within Heap)
  • CSVRowBatch (n+1 ..2n rows)
  • ...
  • CSVStop

Or, signal the middleware that your data is ready in sobjects and let the middleware query (assumes < 50,000 rows unless middleware using bulkAPI) find the data and construct the CSV)

There are, of course, other approaches but this would be enough to get one started.

| improve this answer | |
0

We have implemented a REST API which allow to directly integrate Salesforce with SFTP Server. You can directly upload file from salesforce to SFTP Server using this REST API.

Here we are addng API documentation link. (Note: this link will never change)

FTP API: https://www.ftp-api.com/

Here we are adding upload example with code snippts.

#1. FTP_UploadFile.cmp

<aura:component implements="force:lightningQuickAction,force:hasRecordId"
            access="global"
            controller="FTPWebServiceCompCtrl">
<!--attribute-->
<aura:attribute name="showSpinner" type="boolean" default="false"/>
<aura:attribute name="oFTPResponseWrapper" type="FTPResponseWrapper"/>

<!--handler-->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>    

<!--Markup-->
<aura:if isTrue="{!v.showSpinner}">
    <div>
        <lightning:spinner alternativeText="Loading" size="large" />
    </div>
</aura:if>

<aura:if isTrue="{!v.oFTPResponseWrapper != null}">
    <div class="{!(v.oFTPResponseWrapper.Status == 'ERROR'?'slds-notify slds-notify_alert slds-theme_alert-texture slds-theme_error':'slds-notify slds-notify_alert slds-theme_alert-texture slds-theme_success')}" role="alert">
        <span class="slds-assistive-text">error</span>
        <span class="{!(v.oFTPResponseWrapper.Status == 'ERROR'?'slds-icon_container slds-icon-utility-error slds-m-right_x-small':'slds-icon_container slds-icon-utility-user slds-m-right_x-small')}">
            <lightning:icon iconName="{!(v.oFTPResponseWrapper.Status == 'ERROR'?'utility:error':'utility:upload')}" size="small" variant="inverse"/>
        </span>
        <h2>{!v.oFTPResponseWrapper.Message} - {!v.oFTPResponseWrapper.Code}</h2>
        <div class="slds-notify__close"></div>
    </div>
    
    <aura:if isTrue="{!v.oFTPResponseWrapper.Files != undefined &amp;&amp; v.oFTPResponseWrapper.Files.length>0}">
        <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_col-bordered slds-m-top_medium">
            <thead>
                <tr class="slds-line-height_reset">
                    <th class="" scope="col">
                        <div class="slds-truncate" title="File Name">File Name</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Path">Path</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Message">Message</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Status">Status</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Code">Code</div>
                    </th>
                </tr>
            </thead>
            <tbody>
                <aura:iteration items="{!v.oFTPResponseWrapper.Files}" var="oFileWrapper">
                    <tr class="{!(oFileWrapper.Status=='ERROR'?'slds-hint-parent has-error':'slds-hint-parent')}">
                        <th data-label="File Name" scope="row">
                            <div class="slds-truncate" title="{!oFileWrapper.FileName}">
                                {!oFileWrapper.FileName}
                            </div>
                        </th>
                        <td data-label="Path">
                            <div class="slds-truncate" title="{!oFileWrapper.Path}">{!oFileWrapper.Path}</div>
                        </td>
                        <td data-label="Message">
                            <div class="slds-truncate" title="{!oFileWrapper.Message}">{!oFileWrapper.Message}</div>
                        </td>
                        <td data-label="Status">
                            <div class="slds-truncate" title="{!oFileWrapper.Status}">{!oFileWrapper.Status}</div>
                        </td>
                        <td data-label="Code">
                            <div class="slds-truncate" title="{!oFileWrapper.Code}">{!oFileWrapper.Code}</div>
                        </td>
                    </tr>
                </aura:iteration>
            </tbody>
        </table>
    </aura:if>
</aura:if>
</aura:component>

FTP_UploadFileController.js

({
doInit : function(component, event, helper) {
    helper.uploadFile(component, event);
}
})

FTP_UploadFileHelper.js

({
uploadFile : function(component, event) {
    this.startWaiting(component);
    var action = component.get("c.uploadFileToFTPServer");
    action.setParams({ recordId : component.get("v.recordId") });
    action.setCallback(this, function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {
            let result = response.getReturnValue();
            component.set("v.oFTPResponseWrapper", result);
        }else if (state === "ERROR") {
            var errors = response.getError();
            if (errors) {
                if (errors[0] && errors[0].message) {
                    this.showToastMessage(component, event, 'Error', errors[0].message, 'error');
                    console.log("Error message: " + errors[0].message);
                }
            } else {
                this.showToastMessage(component, event, 'Error', "Unknown error", 'error');
                console.log("Unknown error");
            }
        }
        this.stopWaiting(component);
    });
    $A.enqueueAction(action);
},

showToastMessage : function(component, event, title, message, type) {
    var toastEvent = $A.get("e.force:showToast");
    toastEvent.setParams({
        "title": title,
        "message": message,
        "type":type,
        "duration":10000
    });
    toastEvent.fire();
},

startWaiting : function(component) {
    component.set("v.showSpinner", true);
},

stopWaiting : function(component){
    component.set("v.showSpinner", false);
}
})

#2. FTPWebServiceCompCtrl.cls

public class FTPWebServiceCompCtrl {

@AuraEnabled
public static FTPResponseWrapper uploadFileToFTPServer(Id recordId){
    try{
        return FTPWebServiceExample.uploadFiles(recordId);
    }catch(Exception ex){
        throw new AuraException(ex.getMessage());
    }
}
}

#3. FTPWebServiceExample.cls

public class FTPWebServiceExample {

public static FTPResponseWrapper uploadFiles(Id parentId){
    List<FileWrapper> lstFileWrapper = new List<FileWrapper>();
    for(Attachment oAttachment : [SELECT Id, body, Name FROM Attachment Where ParentId=:parentId]){
        lstFileWrapper.add(new FileWrapper(oAttachment.Name, '/tmp', EncodingUtil.base64Encode(oAttachment.body)));
    }
    String endpoint = FTPWebServiceUtility.FTP_WebServiceEndpoint+'upload'; 
    HttpResponse response = FTPWebService.uploadFileOnFTPServer(endpoint, 'POST', JSON.serialize(lstFileWrapper));
    if(response != null){
        if(response.getStatusCode() == 200){
            return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class);
        }else if(response.getStatusCode() == 400){
            return (FTPResponseWrapper)JSON.deserialize(response.getBody(), FTPResponseWrapper.class);
        }else{
            return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', response.getStatusCode());
        }
    }else{
        return FTPWebServiceUtility.getFTPResponseWrapper('ERROR', 'Callout Failed!', 0);
    }
}
}

#4. FTPWebService.cls

public class FTPWebService{

//Upload Files on FTP Server
public static HttpResponse uploadFileOnFTPServer(String endpoint, String http_method, String http_body){
    HttpRequest req = createHttpRequest(endpoint, http_method, http_body, FTPWebServiceUtility.oSourceFtpWebServiceConfig);
    Http httpreq = new Http();
    return httpreq.send(req);
}

private static HttpRequest createHttpRequest(String endpoint, String method, String body, FTP_Web_Service_Configuration__c oConfiguration){
    HttpRequest req = new HttpRequest();
    req.setHeader('ftp-host', oConfiguration.Host__c);
    req.setHeader('ftp-type', oConfiguration.Source__c);
    req.setHeader('username', oConfiguration.Username__c);
    req.setHeader('password', oConfiguration.Password__c);
    req.setHeader('port', oConfiguration.Port__c);
    req.setHeader('Content-Type', 'application/json');
    req.setEndpoint(endpoint);
    req.setMethod(method);
    if(String.isNotBlank(body) && String.isNotEmpty(body)){
        req.setBody(body);
    }
    req.setTimeout(120000);
    return req;
}
}

#5. FTPWebServiceUtility.cls

public class FTPWebServiceUtility {

public static String FTP_WebServiceEndpoint = 'https://www.ftp-api.com/ftp/';

public static FTP_Web_Service_Configuration__c oSourceFtpWebServiceConfig{
    get{
        if(oSourceFtpWebServiceConfig == null){
            return getFTPWebServiceConfiguration(false);
        }
        return oSourceFtpWebServiceConfig;  
    }
    set;
}

public static FTP_Web_Service_Configuration__c oTargetFtpWebServiceConfig{
    get{
        if(oTargetFtpWebServiceConfig == null){
            return getFTPWebServiceConfiguration(true);
        }
        return oTargetFtpWebServiceConfig;
    }
    set;
}

public static FTP_Web_Service_Configuration__c getFTPWebServiceConfiguration(Boolean isTargetEnvironment){
    FTP_Web_Service_Configuration__c oWebServiceConfiguration = new FTP_Web_Service_Configuration__c();
    String sSOQL = 'SELECT Id, Host__c, Username__c, Password__c, Port__c, Source__c';
    sSOQL += ' FROM FTP_Web_Service_Configuration__c';
    sSOQL += ' WHERE IsActive__c = true';
    if(isTargetEnvironment){
        sSOQL += ' AND IsTargetEnvironment__c = true';
    }else{
        sSOQL += ' AND IsSourceEnvironment__c = true';
    }
    for(FTP_Web_Service_Configuration__c wsc : Database.query(sSOQL)){
        oWebServiceConfiguration = wsc;
    }
    return oWebServiceConfiguration;
}


public static FTPResponseWrapper getFTPResponseWrapper(String status, String message, Integer code){
    FTPResponseWrapper oWrapper = new FTPResponseWrapper();
    oWrapper.Status = status;
    oWrapper.Message = message;
    oWrapper.Code = code;
    return oWrapper;
}
}

#6. FTPResponseWrapper.cls

public class FTPResponseWrapper {
@AuraEnabled public String Status;
@AuraEnabled public String Message;
@AuraEnabled public Integer Code;
@AuraEnabled public List<FTPFileResponseWrapper> Files;
}

Here we are adding complete github repository link. (Note: repository link will never change) Link:https://github.com/ftprestapi/ftp-api-example

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.