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);
}
<lightning-input type="file" label="Upload" onchange={handleFileUpload}></lightning-input>
? This way the blob lives only in the JS