Article
2 comments

Deploy binary files from SharePoint Hosted App to Host Web

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.

upload-binary-file-to-host-web

Upload binary file to host web

 

Not the app icon can be read correctly in the host web. Otherwise the image will refuse to load.

image-file-in-host-web

Image file after upload to the host web

 

 

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

2 Comments

  1. 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

    Reply

Leave a Reply

Required fields are marked *.


This site uses Akismet to reduce spam. Learn how your comment data is processed.