Skip to content

Instantly share code, notes, and snippets.

@SalesforceBobLightning
Last active March 14, 2024 10:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SalesforceBobLightning/7e244fe097390000c45a7db2a0957aa4 to your computer and use it in GitHub Desktop.
Save SalesforceBobLightning/7e244fe097390000c45a7db2a0957aa4 to your computer and use it in GitHub Desktop.
Salesforce Lightning FileUpload Component

Usage

 <c:FileUpload parentId="" label="" />
<aura:component controller="FileUploadController">
<aura:attribute name="parentId" type="Id" />
<aura:attribute name="label" type="String" />
<aura:attribute name="uploading" type="Boolean" default="false" />
<aura:handler event="aura:waiting" action="{!c.waiting}" />
<aura:handler event="aura:doneWaiting" action="{!c.doneWaiting}" />
<aura:if isTrue="{!v.uploading}">
<lightning:spinner alternativeText="Loading" size="large" />
</aura:if>
<div>
<div class="slds-form-element">
<span class="slds-form-element__label" id="file-selector-primary-label">{! v.label }</span>
<div class="slds-form-element__control">
<div class="slds-file-selector slds-file-selector_files">
<div class="slds-file-selector__dropzone">
<input aura:id="file" class="slds-file-selector__input slds-assistive-text" type="file" id="file-upload-input-01"
aria-labelledby="file-selector-primary-label file-selector-secondary-label" />
<label class="slds-file-selector__body" for="file-upload-input-01" id="file-selector-secondary-label">
<span class="slds-file-selector__button slds-button slds-button_neutral">
<lightning:icon iconName="utility:upload" size="xx-small" alternativeText="upload" />Upload
Files</span>
<span class="slds-file-selector__text slds-medium-show">or Drop Files</span>
<span>
<ui:button label="Upload" press="{!c.save}" />
</span>
</label>
</div>
</div>
</div>
</div>
</div>
</aura:component>
.THIS .notUploading {
visibility: hidden;
}
.THIS .uploading {
visibility: visible;
display: inline-block;
margin-top: 10px;
}
.THIS .small-spinner {
height: 20px;
width: 20px;
}
.THIS .file {
margin-bottom: 5px;
display: block;
}
public without sharing class FileUploadController {
@AuraEnabled
public static Id saveTheFile(Id parentId, String fileName, String base64Data, String contentType) {
base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
ContentVersion contentVersion = new ContentVersion(
versionData = EncodingUtil.base64Decode(base64Data),
title = fileName,
pathOnClient = '/' + fileName,
FirstPublishLocationId = parentId);
insert contentVersion;
return contentVersion.Id;
}
@AuraEnabled
public static Id saveTheChunk(Id parentId, String fileName, String base64Data, String contentType, String fileId) {
if (fileId == '') {
fileId = saveTheFile(parentId, fileName, base64Data, contentType);
} else {
appendToFile(fileId, base64Data);
}
return Id.valueOf(fileId);
}
private static void appendToFile(Id fileId, String base64Data) {
base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
ContentVersion contentVersion = [
SELECT Id, versionData
FROM ContentVersion
WHERE Id = :fileId
];
String existingBody = EncodingUtil.base64Encode(contentVersion.versionData);
contentVersion.versionData = EncodingUtil.base64Decode(existingBody + base64Data);
update contentVersion;
}
}
({
save: function (component, event, helper) {
helper.save(component);
},
waiting: function (component, event, helper) {
component.set("v.uploading", true);
},
doneWaiting: function (component, event, helper) {
component.set("v.uploading", false);
}
})
({
MAX_FILE_SIZE: 4500000, /* 6 000 000 * 3/4 to account for base64 */
CHUNK_SIZE: 950000, /* Use a multiple of 4 */
save: function (component) {
var fileInput = component.find("file").getElement();
var file = fileInput.files[0];
if (file.size > this.MAX_FILE_SIZE) {
alert('File size cannot exceed ' + this.MAX_FILE_SIZE + ' bytes.\n' +
'Selected file size: ' + file.size);
return;
}
var fr = new FileReader();
var self = this;
fr.onload = function () {
var fileContents = fr.result;
var base64Mark = 'base64,';
var dataStart = fileContents.indexOf(base64Mark) + base64Mark.length;
fileContents = fileContents.substring(dataStart);
self.upload(component, file, fileContents);
};
fr.readAsDataURL(file);
},
upload: function (component, file, fileContents) {
var fromPos = 0;
var toPos = Math.min(fileContents.length, fromPos + this.CHUNK_SIZE);
// start with the initial chunk
this.uploadChunk(component, file, fileContents, fromPos, toPos, '');
},
uploadChunk: function (component, file, fileContents, fromPos, toPos, attachId) {
console.log('uploadChunk');
var action = component.get("c.saveTheChunk");
var chunk = fileContents.substring(fromPos, toPos);
action.setParams({
parentId: component.get("v.parentId"),
fileName: file.name,
base64Data: encodeURIComponent(chunk),
contentType: file.type,
fileId: attachId
});
var self = this;
action.setCallback(this, function (a) {
console.log('uploadChunk: Callback');
attachId = a.getReturnValue();
fromPos = toPos;
toPos = Math.min(fileContents.length, fromPos + self.CHUNK_SIZE);
if (fromPos < toPos) {
self.uploadChunk(component, file, fileContents, fromPos, toPos, attachId);
} else {
console.log('uploadChunk: done');
self.showToast('Upload Complete', 'You file has successfully uploaded, please upload another now.', 'success', null);
}
});
$A.getCallback(function () {
$A.enqueueAction(action);
})();
},
showToast: function (title, message, type, icon) {
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
title: title,
message: message,
duration: '2000',
key: icon,
type: type,
mode: 'dismissible'
});
toastEvent.fire();
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment