Upload file in Lightning Component without "parentId" before record Creation
- Oliver Jones
- Aug 17, 2020
- 3 min read
Updated: Oct 15, 2021
In Salesforce lightning, uploading files will require parentId in order to store the uploaded file directly under the parent record, but what if we face a situation when a user submits a form for the first time that requires a file to upload in the first place?

There is a workaround for this, you will need to use <lightning:input> tag replacing standard <lightning:fileupload> tag. This tag does not require a parentId to attach to, so we can just add the file to any parent record whenever and wherever we need to. But this solution comes up with a cost, we will need to write up the file reader code manually in our javascript controllers and helpers. Here is the working code,
Component code:
<div class="slds-modal__header">
<div class="slds-text-heading_medium slds-hyphenate">File Upload</div>
</div>
<div class="slds-modal__content slds-p-around--medium">
<lightning:layoutItem padding="around-small">
<lightning:select name="Document Type" aura:id="inputDocType" label="Document Type" required="true" >
<aura:iteration items="{! v.uploadSelector}" var="documentType">
<option value="{! documentType.value}">{! documentType.text}</option>
</aura:iteration>
</lightning:select>
<lightning:input aura:id="inputUploadFile" onchange="{! c.handleFileUpload}" type="file" label="" name="file" multiple="false" accept=".doc, .docx, .pdf, .png, .jpeg,.jpg,.JPG,.JPEG,.PNG,.DOCX,.DOC,.PDF"/>
<div class="{!'slds-text-body_small slds-text-color_'+ (v.isValidFile ? 'weak' : 'error')}">{! v.fileName} </div>
<div class="slds-text-body_small slds-text-color_error">{! v.showFileUploadError}</div>
</lightning:layoutItem>
</div>
<div class="slds-modal__footer">
<lightning:button variant="brand" label="Upload" onclick="{! c.saveFile }" disabled="{! !v.isValidFile}" />
<lightning:button label="Back" onclick="{! c.cancelUpload }" />
</div>
Controller code:
//This method is called whenever a file upload occurs
handleFileUpload : function (component, event, helper) {
var fileName = 'No File Selected..';
var acceptFormat = event.getSource().get("v.accept").split(",");
var filetype;
var extension;
if(event.getSource().get("v.files").length > 0) {
fileName = event.getSource().get("v.files")[0]["name"];
filetype = fileName.split(".");
extension = "."+filetype[filetype.length-1];
var match = false;
for(var i in acceptFormat) {
console.log('filetype: '+acceptFormat[i] +' == '+ extension);
if(acceptFormat[i].trim() == extension.trim()) {
console.log('Match');
match = true;
break;
}
if(!match) {
console.log("You have uploaded an invalid format file");
fileName = "You have uploaded an invalid format file. Please upload file in the format of .doc, .docx, .pdf, .png, .jpeg'";
component.set("v.isValidFile", false);
}
else {
component.set("v.fileName", fileName);
component.set("v.isValidFile", true);
}
}
}
}
//This method is called whenever user clicks on Upload (the final submit)
saveFile : function(component, event, helper) {
var valid = true;
if(component.find("docType").get("v.value") == null) {
valid = false;
component.find("inputDocType").set("v.errors", [{message:"Document Type is required"}]);
}
if(valid) {
if(component.find("inputUploadFile").get("v.files") != null && component.find("inputUploadFile").get("v.files").length > 0) {
helper.submitRequest(component);
}
}
}
Helper code:
//Call Apex method to insert a new Parent record
submitRequest : function(component) {
var action = component.get("c.submitNewRequest");
action.setParams({
myRec : component.get("v.contactRec"),
});
action.setCallback(this, function(response) {
if(response.getState() == "SUCCESS") {
var parentId = response.getReturnValue();
this.uploadMyFile(component,
component.find("inputUploadFile").get("v.files"), parentId);
}
});
$A.enqueueAction(action);
}
MAX_FILE_SIZE: 4500000, //Max file size 4.5 MB (Customizable)
CHUNK_SIZE: 750000, //Chunk Max size 750Kb
uploadHelper: function(component, fileInput, parentId) {
var file = fileInput[0];
if(file.size > self.MAX_FILE_SIZE) {
component.set("v.showFileUploadError", 'Alert : File size cannot exceed ' + this.MAX_FILE_SIZE + ' bytes.\n' + ' Selected file size: ' + file.size);
return;
}
//create a FileReader object
var objFileReader = new FileReader();
//set onload function of FileReader object
objFileReader.onload = $A.getCallback(function() {
var fileContents = objFileReader.result;
var base64 = 'base64,';
var dataStart = fileContents.indexOf(base64) + base64.length;
fileContents = fileContents.substring(dataStart);
//call the uploadProcess method
self.uploadProcess(component, file, fileContents, parentId);
});
objFileReader.readAsDataURL(file);
},
uploadProcess: function(component, file, fileContents, parentId) {
//set a default size or startpostiton as 0
var startPosition = 0;
//calculate the end size or endPostion using Math.min() function which is return the min. value
var endPosition = Math.min(fileContents.length, startPosition + this.CHUNK_SIZE);
//start with the initial chunk, and set the attachId(last parameter)is null in begin
this.uploadInChunk(component, file, fileContents, startPosition, endPosition, '', parentId);
}
//Method to upload each chunk
uploadInChunk: function(component, file, fileContents, startPosition, endPosition, attachId, parentId) {
//call the apex method 'saveChunk'
var getchunk = fileContents.substring(startPosition, endPosition);
//Call Apex method to insert the attachment
var action = component.get("c.saveChunk");
action.setParams({
parentId: parentId,
fileName: file.name,
base64Data: encodeURIComponent(getchunk),
contentType: file.type,
fileId: attachId,
});
action.setCallback(this, function(response) {
//store the response / Attachment Id
attachId = response.getReturnValue();
if(response.getState() === "SUCCESS") {
//update the start position with end postion
startPosition = endPosition;
endPosition = Math.min(fileContents.length, startPosition + this.CHUNK_SIZE);
//check if the start postion is still less then end postion
//then call again 'uploadInChunk' method ,
//else, diaply alert msg and hide the loading spinner
if(startPosition < endPosition) {
this.uploadInChunk(component, file, fileContents, startPosition, endPosition, attachId);
}
else {
console.log('File uploaded successfully.');
}
}
});
$A.enqueueAction(action);
}
Apex Controller code:
@AuraEnabled
public static Id submitNewRequest(Contact myRec) {
insert myRec;
return myRec.Id;
}
@AuraEnabled
public static Id saveChunk(Id parentId, String fileName, String base64Data, String contentType, String fileId) {
if(fileId == '') {
//Customizable can insert Content Document in place of attachment
base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
Attachment attachRec = new Attachment();
attachRec.parentId = parentId;
attachRec.Body = EncodingUtil.base64Decode(base64Data);
attachRec.Name = fileName;
attachRec.ContentType = contentType;
insert attachRec;
fileId = attachRec.Id;
}
else {
base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
Attachment attachToUpdate = [
SELECT Id, Body
FROM Attachment
WHERE Id =: fileId
];
String existingBody = EncodingUtil.base64Encode(attachToUpdate.Body);
attachToUpdate.Body = EncodingUtil.base64Decode(existingBody + base64Data);
update attachToUpdate;
}
return Id.valueOf(fileId);
}
Commentaires