SharePoint hosted Apps are limited to, use JavaScript or Jquery only. In some cases you like to copy a file directly from a SharePoint hosted app to the host web. This works as long as the file is a simple text file just like a master page or display template.
In case you like to copy an image to the host web, this is not possible trough jQuery. The reason for that is that jQuery has only been capable reading text files but not binary files.
The copy will succeed, but the file will be unusable and destroyed.
Background information on upload binary files
In order to read a binary file from the app web and upload it to the host web a little trick is required. While I struggle with this issue I found a really interesting article by Henrik Algus. In his article “Reading binary data using jQuery ajax” he describes more background information and how to solve it. He also mentioned:
“Sometimes making complete fallback to XMLHttpRequest is not a good idea, especially if you want to keep jQuery code clean and understandable.”
This statement is true and it is better to stay in one tool instead of mixing tools up. I don’t like to mix up pure JavaScript and jQuery too in my code. The solution he provided in his article works pretty well in jQuery.
The SharePoint implementation for uploading binary files
Now let’s use this code in a SharePoint hosted app. This App already consists of a AppIcon.png and we like to transfer it from the app web to the host web. The first step we need to take is to add the transportation support to extend jquery into the App.js file.
// Extends jquery ajaxTransport too support binary reading of files $.ajaxTransport("+binary", function (options, originalOptions, jqXHR) { // check for conditions and support for blob / arraybuffer response type if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) { return { // create new XMLHttpRequest send: function (headers, callback) { // setup all variables var xhr = new XMLHttpRequest(), url = options.url, type = options.type, async = options.async || true, // blob or arraybuffer. Default is blob dataType = options.responseType || "blob", data = options.data || null, username = options.username || null, password = options.password || null; xhr.addEventListener('load', function () { var data = {}; data[options.dataType] = xhr.response; // make callback and send data callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); }); xhr.open(type, url, async, username, password); // setup custom headers for (var i in headers) { xhr.setRequestHeader(i, headers[i]); } xhr.responseType = dataType; xhr.send(data); }, abort: function () { jqXHR.abort(); } }; } });
This allows us to request the image from the app web, and read it as an object called buffer-array and we are able to change the datatype to be binary.
This returned buffer-array cannot be saved directly to a new file in SharePoint. First, we need to transfer it to a base64 encoded string. The following code does this transformation for us and the returned string can be saved to SharePoint.
var arrayBufferToBase64 = function(buffer) { var binary = ''; var bytes = new Uint8Array(buffer); var len = bytes.byteLength; for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); }
Finally, we need to save the file using JSOM which will be done by the following part of the script.
// Read file from app web $.ajax({ url: appWebUrl + sourcePath, type: "GET", dataType: "binary", processData: false, responseType: 'arraybuffer', cache: false }).done(function (contents) { var fileName = getFilenameFromUrl(targetPath); var folder = getPathFromUrl(targetPath); // Create new file var createInfo = new SP.FileCreationInformation(); // Convert ArrayBuffer to Base64 string createInfo.set_content(arrayBufferToBase64(contents)); // Overwrite if already exists createInfo.set_overwrite(true); // set target url createInfo.set_url(fileName); // retrieve file collection of folder var files = hostWebContext.get_web().getFolderByServerRelativeUrl(getRelativeUrlFromAbsolute(hostWebUrl)+folder).get_files(); // load file collection from host web hostWebContext.load(files); // add the new file files.add(createInfo); // upload file hostWebContext.executeQueryAsync(function () { logMessage("File uploaded succeeded", state.SUCCESS); }, function (sender, args) { logMessage("File uploade failed", state.ERROR); }); }).fail(function (jqXHR, textStatus) { logMessage(textStatus, state.ERROR); });
After this code have been executed successfully the appicon.png can be found in the site assets library on the host web.
Not the app icon can be read correctly in the host web. Otherwise the image will refuse to load.
Scenarios to upload binary files
Being able to upload binary files to the host web offers a lot of benefits. This scenario can be used for branding to upload icons, fonts, images, document templates and so on. In short, any binary file can be deployed by a simple SharePoint Hosted app.
The same thing can be accomplished using a provider hosted app too. The problem can be found in the Application Life Cycle. Once the files have been deployed there is no use of this web application anymore. While the app can be removed from SharePoint the web application running in the background still exists and will probably never cleaned up.
The shown scenario also is a great replacement for Sandbox Solution because it gives you better control over your files.
Final toughts
Whenever you need to transfer files into SharePoint the use of a SharePoint hosted app is the best replacement for Sandbox Solutions. To me, it’s even better than using a provider hosted app. This workflow have not much impact on Office 365 and the application can be removed afterwards once the files have been deployed.
Any file, text based or not, can be transferred the binary way. A simple way to publish anything into SharePoint.
Supported browsers
Source – “Reading binary data using jQuery ajax”
BinaryTransport requires XHR2 responseType, ArrayBuffer and Blob response type support from your browser, otherwise it does not work as expected. Currently most major browsers should work fine.
Firefox: 13.0+ Chrome: 20+ Internet Explorer: 10.0+ Safari: 6.0 Opera: 12.10
UDPATE – 8.6.2015: This sample now part of Office 365 Pattern and Practices and contains a reworked source code.
Download Source Code of SharePoint Hosted App to deploy binary files
[…] Deploy binary files from SharePoint Hosted App to Host Web by Stefan Bauer […]
This works great for SPO, but not so much for my on-prem.
I get a “Unexpected response data from server.” error.
I realized since my environment is “http://xxx/sites/test (note: no .com ending on domain), I replaced getRelativeUrlFromAbsolute(hostWebUrl) with “/sites/test/” , but got another error – “File upload failed Value does not fall within the expected range”.
And this was with the PnP solution. Any idea how to fix it? The error happens in the .done() function