Uploading and attaching files to records in Dynamics 365 is a common requirement for web applications. In Power Apps Portals, we can leverage JavaScript and Dynamics 365’s Web API to attach files to file-type fields associated with records. Here, we'll explore the process of creating a file upload form and integrating it with Dynamics 365, allowing users to upload and attach files to fields.
CRM Contact entity Form where we will store file in av_resume field
In this example, we’re creating a simple form with a file input field and an upload button. This form will allow users to select and upload files to Dynamics 365 directly from the Power Apps Portal.
The HTML code below creates a section that includes:
- An `input` element of `type="file"` where users can choose the file they want to upload.
- A button that triggers the file upload when clicked.
Here’s the HTML structure:
<section>
<div class="container" >
<div class="row" >
<div class="col-md-12" >
<div class="form-group" >
<input type="file" id="resume" class="form-control"/ >
</div >
<div class="form-group" >
<button class="form-control" onclick="upload()" >Upload </button >
</div >
</div >
</div >
</div >
</section >
To upload files securely, we first need to retrieve an authentication token. The code below demonstrates how to create a `getToken` function, which uses the `shell.getTokenDeferred()` method to obtain the token. This token will be required to authenticate the file upload request to the Dynamics 365 API.
const getToken = function () {
return new Promise((resolve, reject) => {
shell.getTokenDeferred().done(token => {
resolve(token);
}).fail(error => {
reject(error);
});
});
}
The `uploadFile` function is where the main file upload logic resides. Here’s a breakdown of how this function works:
const uploadFile = async function (fileInputID, fieldLogicalName, guid) {
return new Promise(async (resolve, reject) => {
const fileInput = document.getElementById(fileInputID);
const file = fileInput.files[0];
const token = await getToken();
if (!file) {
resolve({ "success": "No file selected." });
return;
}
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = async function (event) {
const fileArrayBuffer = event.target.result;
const fileName = file.name;
try {
const apiUrl = `/_api/contacts(${guid})/${fieldLogicalName}`;
const response = await fetch(apiUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/octet-stream',
'x-ms-file-name': fileName,
'__RequestVerificationToken': token
},
body: fileArrayBuffer
});
if (!response.ok) {
throw new Error(`Error uploading file: ${response.statusText}`);
}
resolve({ "success": "File uploaded successfully." });
} catch (error) {
reject({ "error": `Error uploading file: ${error}` });
}
};
fileReader.onerror = function () {
reject({ "error": "Failed to read the file. Please try again." });
};
});
};
The `upload` function, triggered by the button click, calls `uploadFile` with the file input ID (`resume`) and the field logical name in Dynamics 365 where the file will be stored (e.g., `av_resume`) and the GUID of record where we will store the file. In our case we are taking GUID of logged in person and will attach file in Resume field.
async function upload() {
const recordGUID = {{ user.id }}; //we will take GUID of loggedin person
var res = await uploadFile('resume', 'av_resume', recordGUID);
console.log(res);
}
Make sure you have Write privilege for related entity