function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
NewSFDCDeveloperNewSFDCDeveloper 

How can we upload files to S3 from Lightning?

How can we upload files to S3 from Lightning?
Best Answer chosen by NewSFDCDeveloper
RituSharmaRituSharma
Add below code in cmp file
----------------------------------------
<lightning:input name="selectedFiles" type="file" label="" onchange="{!c.handleFilesChange}"/>

Add below code in JS Controller file
----------------------------------------
handleFilesChange: function(component, event, helper) {
        component.set('v.showSpinner', true); //To show the spinner
        var files = event.getSource().get("v.files"); 
        var validationFailed = false;
        for(var i=0;i < files.length;i++) {
            if(files[i].name.length > 80){                
                helper.showToastMessage(component,event,$A.get("$Label.c.Error"), $A.get("$Label.c.LongFileName"),"error","dismissible"); //User your labels
                validationFailed = true;
                break;
            }
        }      
        
        if(!validationFailed) {
            helper.uploadFiles(component, event, helper);
        } else {
            component.set('v.showSpinner', false);
        }
    }, 

Add below code in JS Helper file
----------------------------------------
uploadFiles: function(component, event, helper){
     var files = event.getSource().get("v.files");     
     for(var i = 0; i< files.length; i++) {
            var s3Response = {};
            try{                
                var fd = new FormData();     
                var fileName = files[i].name;
                var uploadFileName = component.get("v.recordId")+'-'+fileName.split(' ').join('_');
                var encodedFileName = encodeURIComponent(uploadFileName);     
                s3Response.fileName = fileName;
                fd.append('key', encodedFileName);
                fd.append('AWSAccessKeyId', S3Settings.awsAccessKeyId);
                fd.append('policy', S3Settings.policy);
                fd.append('acl', 'public-read');
                fd.append('signature', S3Settings.signedPolicy); 
                fd.append('file', files[i]);  
                var xhr = new XMLHttpRequest();    
                xhr.onreadystatechange = function() {
                    if (this && this.readyState === 4) {
                        s3Response.statusCode = this.status;
                        s3Response.statusText = this.statusText;
                        s3Response.responseText = this.responseText;                            
                        s3Response.fileURL = 'https://'+S3Settings.host+'/' + encodedFileName;                          
                    }
                };
                xhr.open('POST', 'https://'+S3Settings.host+'/', false); 
                xhr.setRequestHeader("X_FILENAME", fileName);
                xhr.send(fd);     
            }
            catch(e){
                s3Response.error = e.message;
                component.set('v.showSpinner', false);  
                //Need to create log         
            } finally {  
               //Add logic to save the path of files in an object

}

All Answers

RituSharmaRituSharma
Add below code in cmp file
----------------------------------------
<lightning:input name="selectedFiles" type="file" label="" onchange="{!c.handleFilesChange}"/>

Add below code in JS Controller file
----------------------------------------
handleFilesChange: function(component, event, helper) {
        component.set('v.showSpinner', true); //To show the spinner
        var files = event.getSource().get("v.files"); 
        var validationFailed = false;
        for(var i=0;i < files.length;i++) {
            if(files[i].name.length > 80){                
                helper.showToastMessage(component,event,$A.get("$Label.c.Error"), $A.get("$Label.c.LongFileName"),"error","dismissible"); //User your labels
                validationFailed = true;
                break;
            }
        }      
        
        if(!validationFailed) {
            helper.uploadFiles(component, event, helper);
        } else {
            component.set('v.showSpinner', false);
        }
    }, 

Add below code in JS Helper file
----------------------------------------
uploadFiles: function(component, event, helper){
     var files = event.getSource().get("v.files");     
     for(var i = 0; i< files.length; i++) {
            var s3Response = {};
            try{                
                var fd = new FormData();     
                var fileName = files[i].name;
                var uploadFileName = component.get("v.recordId")+'-'+fileName.split(' ').join('_');
                var encodedFileName = encodeURIComponent(uploadFileName);     
                s3Response.fileName = fileName;
                fd.append('key', encodedFileName);
                fd.append('AWSAccessKeyId', S3Settings.awsAccessKeyId);
                fd.append('policy', S3Settings.policy);
                fd.append('acl', 'public-read');
                fd.append('signature', S3Settings.signedPolicy); 
                fd.append('file', files[i]);  
                var xhr = new XMLHttpRequest();    
                xhr.onreadystatechange = function() {
                    if (this && this.readyState === 4) {
                        s3Response.statusCode = this.status;
                        s3Response.statusText = this.statusText;
                        s3Response.responseText = this.responseText;                            
                        s3Response.fileURL = 'https://'+S3Settings.host+'/' + encodedFileName;                          
                    }
                };
                xhr.open('POST', 'https://'+S3Settings.host+'/', false); 
                xhr.setRequestHeader("X_FILENAME", fileName);
                xhr.send(fd);     
            }
            catch(e){
                s3Response.error = e.message;
                component.set('v.showSpinner', false);  
                //Need to create log         
            } finally {  
               //Add logic to save the path of files in an object

}
This was selected as the best answer
NewSFDCDeveloperNewSFDCDeveloper
What's S3Settings variable in your code?
RituSharmaRituSharma
S3Settings I got from APEX. We have stored S3 details(accesskey, bucket name and all) in metadata and those we are getting using APEX.
Tony DeMarco 29Tony DeMarco 29

I know this is a fairly old post, but I have tried to use this code as a guide, and I am getting a CORs error, even though I have set my CORs settings correctly in SF and AWS.

Here is my aura helper:

uploadFiles: function(component, event, helper){
        var s3AccessKeyId;         
        var s3Policy;
        var s3SignedPolicy;
        var s3URL;
        var id = component.get("v.recordId"); 
        var files = event.getSource().get("v.files");  
        var action =  component.get("c.getSettings"); 
        action.setParams({ id : id });
        action.setCallback(this, function(a) {
            var state = a.getState(); console.log("before state");
            if (state == "SUCCESS") {
                component.set("v.s3Settings",a.getReturnValue());
                s3AccessKeyId = component.get("v.s3Settings.accessKeyId");               
                s3Policy = component.get("v.s3Settings.policy"); 
                s3SignedPolicy = component.get("v.s3Settings.signedPolicy"); 
                s3URL = component.get("v.s3Settings.s3URL"); 
                for(var i = 0; i< files.length; i++) {
                    var s3Response = {};
                    try{                
                        var fd = new FormData();       
                        var fileName = files[i].name; 
                        var uploadFileName = component.get("v.recordId")+'-'+fileName.split(' ').join('_');
                        var encodedFileName = encodeURIComponent(uploadFileName);     
                        s3Response.fileName = fileName;
                        fd.append('key', encodedFileName);
                        fd.append('AWSAccessKeyId', s3AccessKeyId);
                        fd.append('policy', s3Policy);
                        fd.append('acl', 'public-read');
                        fd.append('signature', s3SignedPolicy); 
                        fd.append('file', files[i]);  
                        var xhr = new XMLHttpRequest();    
                        xhr.onreadystatechange = function() {
                            if (this && this.readyState === 4) {
                                s3Response.statusCode = this.status;
                                s3Response.statusText = this.statusText;
                                s3Response.responseText = this.responseText;                            
                                s3Response.fileURL = 'https://sft-uploads.s3.amazonaws.com/' + encodedFileName;                          
                            }
                        };
                       
                        xhr.open('POST', 'https://sft-uploads.s3.amazonaws.com/', false); 
                        xhr.setRequestHeader("X_FILENAME", fileName);
                        xhr.send(fd);     
                    }
                    catch(e){
                        s3Response.error = e.message;
                        console.log(s3Response.error);
                        component.set('v.showSpinner', false);  
                        //Need to create log         
                    } finally {  
                        //Add logic to save the path of files in an object
                    }
                }
            } else {
                console.log("Failed at line 246 of helper : ", state);
            }
        });
        $A.enqueueAction(action);
        
    }

And also my APEX Controller
public without sharing  class CaseAttachedS3FilesCntrl {  
    /*public class passes to aura:
     * <aura:attribute name="s3Settings" type="object"
     *               default="{  'bucket': '',
     *                           'region': '',
     *                           's3URL': '',                                
     *                           'accessKeyId': '',
     *                           'policy': '',                                
     *                           'signedPolicy': '',
     *                           'secretAccessKey': ''                                
     *                           }"/> 
     * 
     * make public class match aura attribute s3Settings!
     */
    
    public class S3Settings{
        @AuraEnabled
        public string bucket;
        @AuraEnabled
        public string region;
        @AuraEnabled
        public string s3URL;
        @AuraEnabled
        public string accessKeyId;
        @AuraEnabled
        public string policy;
        @AuraEnabled
        public string signedPolicy;
        @AuraEnabled
        public string secretAccessKey; 
    }
    @AuraEnabled
    public static s3Secret__mdt getS3Credentials(){        
        s3Secret__mdt s3S = s3Secret__mdt.getInstance('sft_uploads')  ;
        System.debug('S3Key ' + s3S.accessKeyId__c);
        return s3S;
    }
    @AuraEnabled
    public static S3Settings getSettings(String id){
        S3Settings s3S = new S3Settings();  
        s3Secret__mdt secrets = getS3Credentials();
        s3S.bucket = secrets.Bucket__c;
        s3S.region = secrets.Region__c;
        s3S.s3URL = secrets.s3_URL__c;
        s3S.accessKeyId = secrets.accessKeyId__c;
        s3S.secretAccessKey = secrets.secretAccessKey__c;
        CaseAttachedS3FilesHelper helper = new CaseAttachedS3FilesHelper();
        System.Debug('params = ' + s3S.bucket + ', ' + s3S.secretAccessKey + ', ' + id);
        s3S.policy = helper.getPolicy(s3S.bucket,s3S.secretAccessKey,id);
        System.Debug('policy = ' + s3S.policy);
        s3S.signedPolicy = helper.getSignedPolicy(s3S.policy);
        return s3S;
    }

  /*there is more code below that has to do with reading an S3 bucket that works, but is not relevant here. So I have Left it out */
}


and finally an Apex helper that signs the policy. 

public class CaseAttachedS3FilesHelper {
    public string Content_Type ='multipart/form-data';
    public string accessType ='private';
    public string bucket;
    public string ForRedirect;
    public string secret;
    public string utype = UserInfo.getUserType();
    public string caseId; //need to retrieve current CaseId
    public string currentHeader; //need to get from current page
    
    //set expiration values
    Datetime expire = system.now().addDays(1);
    String formattedexpire = expire.formatGmt('yyyy-MM-dd')+'T'+ expire.formatGmt('HH:mm:ss')+'.'+expire.formatGMT('SSS')+'Z';           
    
    //set upload policy string
    string policy { get {return 
        '{ "expiration": "'+formattedexpire+'","conditions": [ {"bucket": "'+
        bucket +'" } ,{ "acl": "'+accessType+'" },'+
        '{"content-type":"'+Content_Type+'"},'+
        '{"success_action_redirect": "'+ForRedirect+'"},' +
        '["starts-with", "$key", ""] ]}';   } } 
    
    //get policy and encode
    public String getPolicy(String strBucket,String strSecret, String id) {
        System.debug('2');
        caseId = id;
        bucket = strBucket;
        secret =  strSecret;
        Case c = [SELECT Id from Case where Id =: id];
        //call visualforce controller for parameter 'HOST'
        currentHeader = 'falconstorsf--tdms3new.sandbox.my.salesforce.com';
        System.debug('currentHeader - ' + currentHeader);
        if(utype.containsIgnoreCase('partner')){currentHeader = currentHeader +'/partners';}
        ForRedirect = 'https://'+ currentHeader +'/apex/CaseS3UploadResult?parentIdForRedirect='+caseId;
        return EncodingUtil.base64Encode(Blob.valueOf(policy));
    }
    
    public String getSignedPolicy(String policy) {  

        System.debug('3 - ' + policy);
        return make_sig(EncodingUtil.base64Encode(Blob.valueOf(policy)));        
    }
    //aws signing method
    private String make_sig(string canonicalBuffer) {   
        System.debug('4');
        String macUrl ;
        System.debug('5');
        Blob mac = Crypto.generateMac('HMacSHA1', blob.valueof(canonicalBuffer),blob.valueof(secret)); 
        macUrl = EncodingUtil.base64Encode(mac);  
        System.debug('6');
        return macUrl;
    }
    
}
Aman nemaAman nema
What is policy and signed policy in best answer?
Aman nemaAman nema
Can you please also share the apex code?