In my recent work for a client, there was a need to upload a file to a case not as an attachment but upload under Files Section. As you are aware, Salesforce Files are replacing Attachment. In this post, I am sharing the code end to end for your reference.
In this case, I created a new case and attached file under it, you can also pass a recordId/parentId if you already have the record to which files needs to be attached
FileUpload Component
<aura:component description="FileUpload" implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" controller="FileUploadController">
<aura:attribute name="recordId" type="String" description="Record to which the files should be attached" />
<aura:attribute name="fileName" type="String" default="No File Selected.." />
<lightning:layoutItem class="slds-p-around--xx-small" size="12" smallDeviceSize="12" mediumDeviceSize="12" largeDeviceSize="12">
<div class="slds-grid slds-wrap">
<div class="slds-size_2-of-2">
<lightning:card>
<aura:set attribute="actions">
<button class="slds-button slds-button_neutral">Upload Offline File</button>
</aura:set>
<div class="slds-p-bottom_medium slds-p-left_medium slds-border_bottom">
<div class="slds-media">
<div class="slds-media__figure slds-p-right_medium">
<div class="slds-icon slds-page-header__icon ">
<lightning:icon iconName="action:user" size="small"/>
</div>
</div>
<div class="slds-media__body">
<div class="slds-p-top_small">
<h1>
<span class="slds-page-header__title" title="Upload File">Upload File</span>
</h1>
</div>
</div>
</div>
</div>
<div class="slds-p-around_medium">
<div style="overflow: auto;" >
<div style="min-width: 600px;">
<div class="slds-p-around--small">
<!-- Lightning Input with file type and on file change call the 'handleFilesChange' controller -->
<lightning:input aura:id="fuploader" onchange="{!c.handleFilesChange}" type="file" name="file" label="Upload File" multiple="false"/>
<div class="slds-text-body_small slds-text-color_error">{!v.fileName} </div>
<br/>
<lightning:button label="Save" onclick="{!c.handleSave}"
variant="brand" class="slds-m-top--medium"/>
</div>
</div>
</div>
</div>
</lightning:card>
</div>
</div>
</lightning:layoutItem>
</div>
</aura:component>
Controller Js File
({
handleUploadFinished : function(component, event, helper) {
var uploadedFiles = event.getParam("files");
var documentId = uploadedFiles[0].documentId;
var fileName = uploadedFiles[0].name;
helper.showToast('Success','Success','File Uploaded successfully');
},
handleSave: function(component, event, helper) {
if (component.find("fuploader").get("v.files")!=null && component.find("fuploader").get("v.files").length > 0) {
helper.uploadHelper(component, event);
} else {
helper.showToast('Error','Error','Please Select a Valid File');
}
},
handleFilesChange: function(component, event, helper) {
var fileName = 'No File Selected..';
if (event.getSource().get("v.files").length > 0) {
fileName = event.getSource().get("v.files")[0]['name'];
}
component.set("v.fileName", fileName);
},
})
Helper Js File
({
MAX_FILE_SIZE: 4500000, //Max file size 4.5 MB
CHUNK_SIZE: 750000, //Chunk Max size 750Kb
uploadHelper: function(component, event) {
var fileInput = component.find("fuploader").get("v.files");
var file = fileInput[0];
var self = this;
if (file.size > self.MAX_FILE_SIZE) {
component.set("v.fileName", 'Alert : File size cannot exceed ' + self.MAX_FILE_SIZE + ' bytes.\n' + ' Selected file size: ' + file.size);
return;
}
var objFileReader = new FileReader();
objFileReader.onload = $A.getCallback(function() {
var fileContents = objFileReader.result;
var base64 = 'base64,';
var dataStart = fileContents.indexOf(base64) + base64.length;
fileContents = fileContents.substring(dataStart);
self.uploadProcess(component, file, fileContents);
});
objFileReader.readAsDataURL(file);
},
uploadProcess: function(component, file, fileContents) {
var startPosition = 0;
var endPosition = Math.min(fileContents.length, startPosition + this.CHUNK_SIZE);
this.uploadInChunk(component, file, fileContents, startPosition, endPosition, '');
},
uploadInChunk: function(component, file, fileContents, startPosition, endPosition, attachId) {
var getchunk = fileContents.substring(startPosition, endPosition);
var action = component.get("c.uploadFile");
action.setParams({
fileName: file.name,
base64Data: encodeURIComponent(getchunk),
contentType: file.type,
fileId: attachId
});
// set call back
action.setCallback(this, function(response) {
attachId = response.getReturnValue();
var state = response.getState();
if (state === "SUCCESS") {
// update the start position with end postion
startPosition = endPosition;
endPosition = Math.min(fileContents.length, startPosition + this.CHUNK_SIZE);
if (startPosition < endPosition) {
this.uploadInChunk(component, file, fileContents, startPosition, endPosition, attachId);
} else {
this.showToast('Success','Success','File Uploaded successfully');
}
} else if (state === "INCOMPLETE") {
this.showToast('Error','Error','Issue in uploading File '+response.getReturnValue());
} else if (state === "ERROR") {
var errors = response.getError();
if (errors) {
if (errors[0] && errors[0].message) {
console.log("Error message: " + errors[0].message);
this.showToast('Error','Error','Issue in uploading File '+errors[0].message);
}
} else {
console.log("Unknown error");
}
}
});
// enqueue the action
$A.enqueueAction(action);
},
showToast : function(type, title, message) {
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"type": type,
"title": title,
"message": message
});
toastEvent.fire();
},
})
FileUploadController.cls
public with sharing class FileUploadController {
private static final String ISSUE_UPLOADING_FILE = 'There is a problem in uploading the file';
@AuraEnabled
public static Id uploadFile(String fileName, String base64Data, String contentType) {
Contact instructorContact = UserContactRetriever.getContact();
Case caseObj = new Case(Status='Working',Origin='Community',Subject='Offline Attendance Upload',Contact=instructorContact);
try{
insert caseObj;
base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
ContentVersion cv = createContentVersion(base64Data, filename);
ContentDocumentLink cdl = createContentDocumentLink(cv.Id, caseObj.Id);
if (cv == null || cdl == null) { return null; }
return cdl.Id;
}
catch(Exception exc) {
System.debug('Error: ' + exc.getStackTraceString() + ' ' + exc.getMessage());
Logger.logMessage(exc.getMessage(),'User: '+UserInfo.getName(),'INST_FileUploadController','SaveFile()',String.valueOf(exc.getLineNumber()),exc.getTypeName(), exc.getStackTraceString() ,null);
throw new AuraHandledException(ISSUE_UPLOADING_FILE);
}
}
private static ContentVersion createContentVersion(String base64, String filename) {
ContentVersion cv = new ContentVersion();
cv.VersionData = EncodingUtil.base64Decode(base64);
cv.Title = filename;
cv.ContentLocation = 'S';
cv.PathOnClient = filename;
insert cv;
return cv;
}
private static ContentDocumentLink createContentDocumentLink(String contentVersionId, String recordId) {
if (contentVersionId == null || recordId == null) { return null; }
ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = [
SELECT ContentDocumentId
FROM ContentVersion
WHERE Id =: contentVersionId
].ContentDocumentId;
cdl.LinkedEntityId = recordId;
cdl.ShareType = 'V';
cdl.Visibility = 'InternalUsers';
insert cdl;
return cdl;
}
}