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

Popular posts from this blog

How to show or hide a particular section of Visualforce Page dependent upon picklistfield value

Process Automation Specialist Superbadge

Dynamically Add/Delete rows in Visualforce Page