[SalesForce] How to upload larger size file using input tag of type html in LWC

I have implemented custom file upload component using input file html tag and rendering it as browse file button.

The issue with component is:-

  • When trying to upload file more than 4 mb of size, i am getting
    below error:-

[AuraClientInputException from server] Unexpected request input.
Expected input format: "Max message parameter length is 4194304
characters.".

It seems apex heap size error.

How i am reading the file uploaded?

I am using the FileReader Api to read it and fire an apex method to save the file as ContentVersion Object record in Salesforce.

this.fileReader = new FileReader();
            // set onload function of FileReader object  
            this.fileReader.onloadend = (() => {
                this.filecontent = this.fileReader.result;
                let base64 = 'base64,';
                this.content = this.filecontent.indexOf(base64) + base64.length;
                this.filecontent = this.filecontent.substring(this.content);
                //saving the file by calling an apex method
                saveTheFile({
                    parentId: 'a8pq00000001w4QAAQ', fileName: this.fileName,
                    base64Data: encodeURIComponent(this.filecontent)
                }).then(result => {
                    window.console.log('result ====> ' + result);
                })
                    .catch(error => {
                        // Showing errors if any while inserting the files
                        window.console.log(error);
                    });
            });
            this.fileReader.readAsDataURL(this.file);

Why i can't use Lightning FileUpload component?

This component uploads the document as soon user select the file in browse window,but our aim is to upload files only when we
click a specific upload button.
It also does not have maximum file size attribute.

Best Answer

You'll need to implement workarounds as of now if you need custom functionality w.r.t uploading files.

The max content limit for AuraEnabled methods as mentioned by Pranay Jaiswal is 4MB, which is also reduced when it is encoded into base64 and gives you a final size limit around 1.5 -2 MB that you'll be able to upload.

One of the workarounds would involve implement using Chatter Rest API and it will support file sizes upto 2GB as mentioned by Bryan Anderson.

Here's what I got for this question, I recently had one such requirement which needed upto 30MB of files to be uploaded using input(type=file). Using AuraEnabled was out of question and I didn't really have time to implement a solution using Chatter Rest API. So, I implemented this:

The solution involves using The AJAX toolkit & a Visualforce Page which helped me overcome the API enabled Session issue withing lightning context.

The html file:

<template>

    <div class="slds-hide">
        <iframe src="/apex/uploadHelper"></iframe>
    </div>

    <lightning-input type="file" label="Attachment" onchange={handleFilesChange}> 
    </lightning-input>
        
     <button onclick={fireToVF}> fire</button>
</template>

The JS controller:

import { LightningElement } from 'lwc';

export default class ApiConnector extends LightningElement {
    recordid = '<parent record id>';

    connectedCallback(){
        window.addEventListener('message', (message)=>{
            console.log(message.data);
        });
    }

    toBase64 = file => new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = () => {
            let result = reader.result;
            let base64 = 'base64,';
            let content = result.indexOf(base64) + base64.length;
            let fileContents = result.substring(content);
            resolve(fileContents);
        }
        reader.onerror = error => reject(error);
    });

    async handleFilesChange(event){
        if (event.target.files.length > 0) {
            this.file = {
                parentRec : this.recordid,
                fileName : event.target.files[0].name,
                fileContent : await this.toBase64(event.target.files[0])
            }
        }
    }

    fireToVF(){
        this.fireToComponent(this.file);
    }

    fireToComponent(message){
        this.template.querySelector('iframe').contentWindow.postMessage(message, '*');
    }
}

The Visualforce Page within iframe:

<apex:page showHeader="false" sidebar="false" applyBodyTag="false" standardStylesheets="false" >
    
    <script type="text/javascript">__sfdcSessionId = '{!$Api.Session_Id}';</script>
    <script src="../../soap/ajax/30.0/connection.js" type="text/javascript"></script>
    
    <script>
    function uploadContentVersion(recordId ,filename, filecontent) {
        var contentVersion = new sforce.SObject('ContentVersion');
        
        contentVersion.Title = filename;
        contentVersion.PathOnClient = '/' + filename;
        contentVersion.FirstPublishLocationId = recordId;
        contentVersion.VersionData = filecontent;
        
        var results = sforce.connection.create([contentVersion]);
        for (var i = 0; i < results.length; i++) {
            if (results[i].getBoolean("success")) {
                alert('New ContentVersion record created:' + results[i].id);
            }
            else {
                alert('Failed:' + results[i]);
            }
        }
    }
    
    window.addEventListener('message', (message)=>{
        console.log(message.data);
        var file = message.data;
        uploadContentVersion(file.parentRec, file.fileName , file.fileContent);
    });
        
    window.parent.postMessage('loaded', "*")
    </script>
    
    <body></body>
</apex:page>

Note that this is just an example and may not practice the security requirements required for PostMessage API(checks for Origin and Source).

Since the AJAX toolkit uses SOAP API internally, it has a max limit of 50MB, which due to Base64 Encoding will be reduced from 30-35MB depending on the content in the document.

I had also tried getting the API enabled sessionId from Visualforce page and hitting the Rest api directly from LWC, but the POST fetch request returned 302 and redirected the browser to make a GET request, I'm curious if SF allows POST method over Rest API from LWC/Lightning Context.

Related Topic