0

I am trying to build a Custom JSON to Records Import from a Record List view page.

  1. On the Click of a Button or Action Link,
  2. a File Upload should be displayed in a modal Lightning modal and
  3. the Blob should be passed directly to an Apex method.

I tried with File Upload components, but they always require the File to be stored as a File of a Record. I want this only to be temporary.

Is that possible or feasible? Or would you do it differently?

I found that, but its code and documents are so hard to decipher that I gave up after a few hours of extracting the core ideas: https://unofficialsf.com/from-josh-dayment-improved-file-upload-in-flow-screens/

3
  • 1
    Did you try something like <lightning-input type="file" label="Upload" onchange={handleFileUpload}></lightning-input>? This way the blob lives only in the JS
    – RubenDG
    Jul 18 at 9:21
  • No I did not yet try. Do you have a sample code that shows how to pass the Blob to Apex from JS? Jul 18 at 9:22
  • 1
    Of course, I'm going to post it as answer
    – RubenDG
    Jul 18 at 9:27

1 Answer 1

1

You could use a lightning-input with type="File":

To retrieve the list of selected files, use event.target.files in the onchange event handler. Your selected files are returned in a FileList object, each specified as a File object with the size and type attributes.

This way no ContentDocument is created unless you explictly do so in an apex method.

HTML

<lightning-input
    type="file"
    label="Upload"
    accept={acceptedFormats}
    onchange={handleUploadFile}>
</lightning-input>

Text File

In the JS you could read it creating an instance of a FileReader and calling .readAsText(File, encoding). If encoding parameter is not specified, it defaults to UTF-8.
When the read operation is complete it will fire a loadend event, so you have to define your own function to handle it and the result property will contain the contents of the file as a text string.

CSV

JS

acceptedFormats = ['.csv'];

handleUploadFile(event) {
    if (event.detail.files && event.detail.files.length) {
        const csvFile = event.detail.files[0];
        // you could check the file size (csvFile.size)
        
        this.showSpinner = true;
        const fileReader = new FileReader();
        fileReader.onloadend = () => {
            const fileBody = fileReader.result;
            
            const apexParams = {
                fileName: csvFile.name,
                fileBody
            };
            saveCsvFile(apexParams); // call to apex
            this.showSpinner = false; // this should be in the then() of apex call
        };
        fileReader.readAsText(csvFile);
    }
}

Apex:

@AuraEnabled
public static void saveCsvFile(String fileName, String fileBody) {
    System.debug('Reading ' + fileName + ' file');
    List<String> rows = fileBody.split('\n');
    for (String row : rows) {
        System.debug('Row: ' + row);
    }
}

JSON

If you're reading a JSON, I would suggest to create an apex class to use typed deserialization.
In JS you could read it with readAsText then parse it to get an object and use this one as input parameter for your apex method.

JS

acceptedFormats = ['.json'];

handleUploadFile(event) {
    if (event.detail.files && event.detail.files.length) {
        const jsonFile = event.detail.files[0];
        
        this.showSpinner = true;
        const fileReader = new FileReader();
        fileReader.onloadend = () => {
            const jsonObj = JSON.parse(fileReader.result);
            
            const apexParams = {
                fileName: jsonFile.name,
                myWrapper: jsonObj
            };
            handleJsonFile(apexParams)
                .then(() => { /* show toast or something */ })
                .catch((error) => { /* handle error */ })
                *finally(() => {
                    this.showSpinner = false;
                });
        };
        fileReader.readAsText(jsonFile);
    }
}

Apex:

@AuraEnabled
public static void handleJsonFile(String fileName, MyWrapperClass myWrapper) {
    System.debug('Reading ' + fileName + ' file');
    System.debug(myWrapper);
}

Binary File

If you have to read an image or a binary file and want to pass the base64 to apex, you could use readAsDataURL():

acceptedFormats = ['.jpg', '.jpeg', '.gif', '.png'];

handleUploadFile(event) {
    if (event.detail.files && event.detail.files.length) {
        const imgFile = event.detail.files[0];
        // you could check the file size (imgFile.size)
        
        this.showSpinner = true;
        
        const fileReader = new FileReader();
        fileReader.onloadend = (() => {
            const apexParams = {
                fileName: imgFile.name,
                fileType: imgFile.type
            };
            let result = fileReader.result;
            const base64 = 'base64,';
            const i = result.indexOf(base64) + base64.length;
            apexParams.base64Body = result.substring(i);
            saveImgFile(apexParams); // call to apex
            this.showSpinner = false; // this should be in the then() of apex call
        });

        fileReader.readAsDataURL(imgFile);
    }
}

Please note that after Base64 encoding, the size of the data is increased to ~4/3 of the original. Wiki:

the final size of Base64-encoded binary data is equal to 1.37 times the original data size + 814 bytes (for headers).

Since you're passing a base64 string to apex, in order to get the blob you should call EncodingUtil.base64Decode(base64String)

Apex:

@AuraEnabled
public static void saveImgFile(String fileName, String fileType, String base64Body) {
    Blob fileBody = EncodingUtil.base64Decode(base64Body);
    
    ContentVersion cv = new ContentVersion();
    cv.Title = fileName;
    cv.PathOnClient = fileName;
    cv.VersionData = fileBody;
    cv.IsMajorVersion = true;
    insert cv;
    
    // if you have to send it via REST Api you could use
    // request.setBodyAsBlob(fileBody);
}
3
  • Could you also add a bit of the Apex to show how to handle the Blob content please? Jul 18 at 10:05
  • 1
    @RobertSösemann I added few examples for both text and binary files.
    – RubenDG
    Jul 18 at 11:13
  • There are limits around the size of file you can upload via this approach, ~4.5 MB max. Due to lightning to apex restrictions. Nov 30 at 6:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .