Custom File Upload to Salesforce using LWC and Apex
Hi Everyone,
In this blog, we'll explore custom way of uploading a file to salesforce using Apex and lightning-input tag.
A file can be uploaded using either lightning-file-upload component or lightning-input (having type attribute as "file") component.
Only difference between lightning-file-upload and lightning-input with type as 'file' is that files uploaded by using file-upload component directly get attached to records without writing any additional apex code, whereas you need to write Apex code for attaching files to record while using lightning-input component.
Some important points
- Multiple files can be uploaded by having multiple attribute in component definition.
- All types of files are accepted and furthermore you can restrict what kind of file you want to upload by specifying valid file formats in accept attribute. Ex:- [".pdf", ".doc", ".jpg"]
- Uploaded files can be associated to a record by specifying record-id attribute. If record-id attribute is missing, files would be private to uploading user.
- By default, 10 files can be uploaded simultaneously unless this limit is changed by your admin. Standard limit is from minimum [1] - maximum [25]. Maximum file size limit is 2 GB.
Usage Consideration
- lightning-file-upload doesn't support multiple file upload at once in Android devices.
- If the Don't allow HTML upload as attachments or document records security setting is enabled for your org, file uploader cannot be used to upload file with following files :- [.htm, .html, .htt, .htx, .mhtm, .mhtml, .shtm, .shtml, .acgi, .svg ]
- Having one or more record types on ContentVersion object enables file uploader to provide file details and any error message from the server. if record types are not available , you will see generic error message, "Can't upload the file".
- On mobile devices, file upload doesn't work if any custom field is required on ContentVersion object.
- Click on Official Document to learn more in detail about file upload.
We'll also be implementing below custom validations to our file upload functionality:-
- Maximum file size validation : [File size cannot exceed than defined maximum size ].
- File name validation : [ File name should be alphanumeric only ].
- Only one dot in filename validation : [ Ex:- FileName.extension ].
- File format validation : [ upload component should accept only predefined file types ].
As we are not using standard lightning-file-upload component, so we have write an Apex Class that attaches the uploaded file to a record.
Apex Class (CustomFileUploader)
public with sharing class CustomFileUploader {
@AuraEnabled
public static void saveTheFile(string parentId, string fileName, string base64Data) {
base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
ContentVersion cv = new ContentVersion();
cv.Title = fileName;
cv.VersionData = EncodingUtil.base64Decode(base64Data);
cv.PathOnClient = '/'+fileName;
cv.IsMajorVersion = false;
insert cv;
cv = [Select Id,ContentDocumentId from ContentVersion where Id =: cv.Id];
ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = cv.ContentDocumentId;
cdl.LinkedEntityId = parentId;
cdl.ShareType = 'V';
cdl.Visibility = 'AllUsers';
insert cdl;
}
}
Lightning Web Component (customFileUploader)
<template>
<template lwc:if={doTheSpinner}>
<lightning-spinner size="medium" alternative-text="Loading..."></lightning-spinner>
</template>
<lightning-card title="Custom File Uploader" icon-name="custom:custom45">
<div class="slds-p-medium_large">
<lightning-input type="file"
label="Upload File"
onchange={handleFileUpload}
></lightning-input>
</div>
</lightning-card>
</template>
import { LightningElement, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import saveTheFile from '@salesforce/apex/CustomFileUploadController.saveTheFile';
const FILE_SETTING = {
MAX_FILESIZE_MB : 4,
MAX_FILESIZE_BYTE : 4000000
};
export default class CustomFileUploader extends LightningElement {
@api recordId;
doTheSpinner = false;
fileFormats = ['pdf', 'jpg', 'jpeg', 'png', 'txt', 'doc'];
fileName;
handleFileUpload(event) {
const files = event.target.files;
this.doTheSpinner = true;
let file = files[0];
this.fileName = file.name;
//filesize validation
if(Math.round(file.size / 1000000 ) >= FILE_SETTING.MAX_FILESIZE_MB) {
this.doTheSpinner = false;
const message = `File Size cannot be exceed than ${FILE_SETTING.MAX_FILESIZE_MB} MB`;
this.openToast(message, 'info');
return;
}
//filename validation. [Filename should be alphanumeric only].
const fileRegex = new RegExp(/[^a-zA-z0-9]/);
let fileNameRslt = fileRegex.test(file.name); // This methods returns true if file name is not ALPHANUMERIC otherwise false.
if(!fileNameRslt) {
this.doTheSpinner = false;
const message = 'File name should be alphanumeric only.';
this.openToast(message, 'info');
return;
}
//Only one dot in filename validation.
let fileNameArray = file.name.split('.');
if(fileNameArray.length > 2) {
this.doTheSpinner = false;
const message = 'File name should have only one dot.Ex- [FileName.extension]';
this.openToast(message, 'info');
return;
}
//file format validation. Apply only if file name is correct.
if(fileNameArray.length < 2) {
const typeOfFile = fileNameArray[1];
if(! this.fileFormats.includes(typeOfFile)) {
this.doTheSpinner = false;
const message = 'File type is not acceptable. Upload Only [pdf, jpg, jpeg, png, doc, docx, tiff] files.';
this.openToast(message, 'info');
return;
}
}
var reader = new FileReader();
var self = this;
reader.onload = function() {
let fileContent = reader.result;
let base64Str = 'base64,';
let dataString = fileContent.indexOf(base64Str) + base64Str.length;
let fileContentForServer = fileContent.substring(dataString);
self.callSaveFile(fileContentForServer);
};
reader.readAsDataURL(file);
}
callSaveFile (fileContentForServer) {
//console.log('callSaveFile called and parentId :', this.recordId, 'fileName :', this.fileName);
saveTheFile({
parentId : this.recordId,
fileName : this.fileName,
base64Data : encodeURIComponent(fileContentForServer)
})
.then(result => {
this.doTheSpinner = false;
this.openToast('File Uploaded Successfully', 'success');
})
.catch(error => {
this.doTheSpinner = false;
console.log('error ::', error);
this.openToast(error.body.message, 'error');
});
}
openToast(messageStr, variantType) {
this.dispatchEvent(
new ShowToastEvent({
title : 'File Upload info',
message : messageStr,
variant : variantType
})
);
}
}
Thanks for reading, Always Aloha :)
Comments
Post a Comment