In this article I will demonstrate and discuss how to programmatically save a Word document directly into Salesforce.
Introduction
In previous articles we have looked at how to programmatically interact with a Word document from within an Office Add-In. In this case we will use the Office JavaScript API to access the file as a binary string and then convert it into a format compatible with Salesforce.com.
Turning your word document into a binary
This article in the Office dev center details how to Get the whole document from an add-in for PowerPoint or Word. We will use the bulk of that article up to the point where we send the slice. We are going to look at a modified sendSlice() function. The dev.office.com article details how you can access the binary contents of the word document, but falls short when it comes to getting the base64 encoded version of the file to be submitted to an external site. This is the complicated part !
Getting the right format for Salesforce
Unfortunately the salesforce documentation is not based around using JavaScript to access the REST APIs. It is based around using curl commands in UNIX. The sample page for Inserting or updating Blob Data explains how to upload a multipart message which includes both the meta-data values and the binary data for the file. For the record they also neglect to provide any guidance on how to get the binary of the file….!
Using the code posted on this stackoverflow post we are able to turn a binary string into the necessary format to send the file to salesforce.
The final sendSlice() function is shown below. The significant thing to note is that the initial file must be sent to the ContentVersion sobject. Because this is a new document a parentId is not necessary and a ContentDocumentId is created. In a future article we will look at how to then associate this new file attachment is an existing object.
function sendSlice(slice, state) { updateStatus("In sendSlice"); var data = slice.data; // If the slice contains data, create an HTTP request. if (data) { // Encode the slice data, a byte array, as a Base64 string. // NOTE: The implementation of myEncodeBase64(input) function isn't // included with this example. For information about Base64 encoding with // JavaScript, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding. var u8 = new Uint8Array(data); var b64encoded = btoa(String.fromCharCode.apply(null, u8)); var objectData = { "Description": "Demo Upload Directly from Word", "Title": "sample.docx", "ReasonForChange": "Initial Upload", "PathOnClient": "sample.docx" } var boundary = 'boundary_string_' + Date.now().toString(); var attachmentContentType = 'application/octet-stream'; // Serialize the object, excluding the body, which will be placed in the second partition of the multipart/form-data request var serializedObject = JSON.stringify(objectData); var requestBodyBeginning = '--' + boundary + '\r\n' + 'Content-Disposition: form-data; name="entity_attachment";' + '\r\n' + 'Content-Type: application/json' + '\r\n\r\n' + serializedObject + '\r\n\r\n' + '--' + boundary + '\r\n' + 'Content-Type: ' + attachmentContentType + '\r\n' + 'Content-Disposition: form-data; name="VersionData"; filename="filler"' + '\r\n\r\n'; var requestBodyEnd = '\r\n\r\n' + '--' + boundary + '--'; // The atob function will decode a base64-encoded string into a new string with a character for each byte of the binary data. var byteCharacters = window.atob(b64encoded); // Each character's code point (charCode) will be the value of the byte. // We can create an array of byte values by applying .charCodeAt for each character in the string. var byteNumbers = new Array(byteCharacters.length); for (var i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } // Convert into a real typed byte array. (Represents an array of 8-bit unsigned integers) var byteArray = new Uint8Array(byteNumbers); var totalRequestSize = requestBodyBeginning.length + byteArray.byteLength + requestBodyEnd.length; var uint8array = new Uint8Array(totalRequestSize); var i; // Append the beginning of the request for (i = 0; i < requestBodyBeginning.length; i++) { uint8array[i] = requestBodyBeginning.charCodeAt(i) & 0xff; } // Append the binary attachment for (var j = 0; j < byteArray.byteLength; i++, j++) { uint8array[i] = byteArray[j]; } // Append the end of the request for (var j = 0; j < requestBodyEnd.length; i++, j++) { uint8array[i] = requestBodyEnd.charCodeAt(j) & 0xff; } //Create the new file in Salesforce var url = "https://yourdomain.my.salesforce.com/services/data/v37.0/sobjects/ContentVersion/" return $.ajax({ method: "POST", url: url, processData: false, data: uint8array.buffer, headers: { "Content-Type": 'multipart/form-data' + "; boundary=\"" + boundary + "\"", "Authorization": "Bearer " + app.getCookie(app.addinName) } }).error(function (data) { updateStatus("FAIL: " + JSON.stringify(data)) }).success(function (data) { updateStatus("Success creating ContentVersion: " + data.id) closeFile(state) }); } }
Conclusion
In this article we have seen an example of how we can extend the functionality of an Office Add-In to access the binary data of a word document and then save it to a cloud provider (in this case Salesforce).