2

I am trying to send uploaded file via a JSON. However I am unable to send the file attribute directly in below way.

<template>
                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-input type="text" name="firstName" label="First Name" placeholder="type here..." onchange={handleFormInputChange} required>
                        </lightning-input>
                    </div>

                    <div class="slds-p-around_medium lgc-bg">
                        <lightning-input type="file" label="Attachment" accept="image/png, image/jpg, .zip" onchange={handleFormInputChange}></lightning-input>
                    </div>
</template>

JS file:

    this.firstName;
    this.file;
    handleFormInputChange(event) {

    if(event.target.name == 'firstName'){
       this.firstName = event.target.name;
    }

        if (event.target.files) {
            this.file = event.target.files[0];
        }

        **const data = JSON.stringify({firstName: this.firstName,file: this.file});**
        const nextEvnt = new CustomEvent('initate', {
            detail: { data }
        });
        this.dispatchEvent(nextEvnt);

    }

I tried using below approach that I saw at this link - https://www.salesforcecodecrack.com/2019/06/custom-file-upload-in-lightning-web.html

However, this.fileReader.onloadend gets called only after I form the JSON.

this.fileReader= new FileReader();
// set onload function of FileReader object  
this.fileReader.onloadend = (() => {
    this.fileContents = this.fileReader.result;
    let base64 = 'base64,';
    this.content = this.fileContents.indexOf(base64) + base64.length;
    this.fileContents = this.fileContents.substring(this.content);

    // call the uploadProcess method 
    this.saveToFile();
});

this.fileReader.readAsDataURL(this.file); 

1 Answer 1

3

You need to read the file asynchronously, because otherwise the browser would "freeze" while trying to read the file (JavaScript is a single-threaded model by intention, hence the need for asynchronous file reads).

You would end up doing something like this:

export default class MyWebComponentName extends LightningElement {
    async handleFormInputChange(event) {
        if(event.target.files) {
            const fileName = event.target.files[0].name;
            try {
                const fileContent = await this.readFile(event.target.files[0]);
                const initEvent = new CustomEvent(
                    'initate',
                    {
                        detail: {
                            fileName: fileName,
                            body: fileContent
                        }
                    }
                );
                initEvent.fire();
            } catch(error) {
                // Show an error to the user... not a log 😁
                console.log(error);
            }
        }
    }
    readFile(file) {
        return new Promise((resolve, reject) => {
            const fileReader = new FileReader();
            fileReader.onload = () => {
                resolve(fileReader.result.split(/base64,/)[1]);
            };
            fileReader.onerror = () => {
                reject(fileReader.error);
            };
            fileReader.readAsDataURL(file);
        });
    }
}

Notice how I use await to create a promise to read the file, then process the results after the file is read. This is a pretty bare-bones example, but it should get you started. Note that we're also not handling the possibility of multiple files or other errors, this is just a proof of concept for you to get started with.

2
  • wonderful. Thanks!! Learned something very useful today. However is there any precaution/concern using this way. I assume we are pausing the thread execution until a promise is returned. Would this impact performance or is there any other implication of this approach?
    – SfdcBat
    Apr 12, 2020 at 2:56
  • 1
    @SfdcBat No, the asynchronous nature of file reading is purpose-built to avoid "pausing" the main JS thread; it simply sets aside the work within a closure, goes on to handle other things, then comes back to the file reader once the results are available. P.S. I've written something like this in an Aura component to handle multiple files, it works just fine.
    – sfdcfox
    Apr 12, 2020 at 3:07

You must log in to answer this question.

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