Uploading Files in SharePoint 2013 using CSOM and REST 

Tags: REST and CSOM, SharePoint 2013

Recently, I have been working through the process of trying to accomplish as many basic SharePoint tasks with both CSOM and REST as possible. My goal is to build a deeper understanding of what operations work in both approaches as well as strengths and limitations. In the case of document uploading, the CSOM approach is only good for files up to 1.5MB whereas REST is good up to 2GB. This makes understanding the REST approach critical, but I was struggling to get it working using the jQuery ajax method because the documentation is not great. In this post, I’ll save you the heartache I went through and just show you how to do it in both approaches.

Selecting Files

The first thing to set up is the control used for selecting files from the web page. This is a pretty simple use of input controls:

<input id="inputFile" type="file" />

<input id="uploadDocumentButton" type="Button" value="Upload Document"/>

The inputFile control allows for the browsing and selecting of files. The uploadDocumentButton control initiates the upload process. The inputFile control has a Files collection that you can use to access the file for uploading. The following code shows how to get the filename and file for uploading.

$("#uploadDocumentButton").click(function () {

 

    if (document.getElementById("inputFile").files.length === 0) {

        alert("Select a file!");

        return;

    }

 

    var parts = document.getElementById("inputFile").value.split("\\");

    var filename = parts[parts.length - 1];

    var file = document.getElementById("inputFile").files[0];

}

Reading Files

Once the file is selected, you have to read the bytes into your JavaScript code. This is accomplished using the FileReader object. This object accepts the file information for loading asynchronously. The onload and onerror events fire when the file is loaded successfully or fails. I wrote a helper function using promises to read the file into an ArrayBuffer, which will be used later during the upload.

var getFileBuffer = function (file) {

    var deferred = $.Deferred();

    var reader = new FileReader();

    reader.onload = function (e) {

        deferred.resolve(e.target.result);

    }

    reader.onerror = function (e) {

        deferred.reject(e.target.error);

    }

    reader.readAsArrayBuffer(file);

    return deferred.promise();

};

Uploading with CSOM

Uploading the file with CSOM requires converting the ArrayBuffer to a Base64-encoded array, which is then put into a SP.FileCreationInformation object. The following code shows a complete library for uploading with CSOM.

"use strict";

 

var WingtipToys = window.WingtipToys || {};

WingtipToys.Jsom = WingtipToys.Jsom || {};

 

WingtipToys.Jsom.Libs = function () {

 

    var deferreds = new Array(),

 

    upload = function (serverRelativeUrl, filename, file) {

        deferreds[deferreds.length] = $.Deferred();

 

        getFileBuffer(file).then(

            function (buffer) {

                var bytes = new Uint8Array(buffer);

                var content = new SP.Base64EncodedByteArray(); //base64 encoding

                for (var b = 0; b < bytes.length; b++) {

                    content.append(bytes[b]);

                }

                var ctx = new SP.ClientContext.get_current();

                var createInfo = new SP.FileCreationInformation();

                createInfo.set_content(content); //setting the content of the new file

                createInfo.set_overwrite(true);

                createInfo.set_url(filename);

                this.files = ctx.get_web().getFolderByServerRelativeUrl(serverRelativeUrl).get_files();

                ctx.load(this.files);

                this.files.add(createInfo);

                ctx.executeQueryAsync(

                    Function.createDelegate(this,

                        function () { deferreds[deferreds.length - 1].resolve(this.files); }),

                    Function.createDelegate(this,

                        function (sender, args) { deferreds[deferreds.length - 1].reject(sender, args); }));

 

            },

            function (err) {

                deferreds[deferreds.length - 1].reject(err);

            }

         );

 

        return deferreds[deferreds.length - 1].promise();

 

    },

 

    getFileBuffer = function (file) {

        //See previous code

    };

 

    return {

        upload: upload,

    };

 

}();

Once the library is written, uploading the file becomes pretty simple. For this example, I am assuming a document library named “JSOM Documents” exists. Notice how the server-relative URL is provided to the upload method.

WingtipToys.Jsom.Libs.upload("/apps/LibraryOperations/JSOM%20Documents", filename, file)

.then(

    function (files) {

        alert("Uploaded successfully");

    },

    function (sender, args) {

        alert(args.get_message());

    }

);

Uploading with REST

The REST approach can use the contents of the ArrayBuffer directly. The key to making this approach work with jQuery ajax is to set the processData flag to false. By default, jQuery ajax will process all non-string data into a query string, which corrupts the binary file data during the upload. By setting it to false, the data is faithfully uploaded. The following code shows a complete library for uploading with REST.

"use strict";

 

var WingtipToys = window.WingtipToys || {};

WingtipToys.Rest = WingtipToys.Rest || {};

 

WingtipToys.Rest.Libs = function () {

 

    var upload = function (serverRelativeUrl, filename, file) {

 

        var deferred = $.Deferred();

 

        getFileBuffer(file).then(

 

            function (arrayBuffer) {

 

                $.ajax({

                    url: _spPageContextInfo.webServerRelativeUrl +

                        "/_api/web/GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Files" +

                        "/Add(url='" + filename + "', overwrite=true)",

                    type: "POST",

                    data: arrayBuffer,

                    processData: false,

                    headers: {

                        "accept": "application/json;odata=verbose",

                        "X-RequestDigest": $("#__REQUESTDIGEST").val(),

                        "content-length": arrayBuffer.byteLength

                    },

                    success: function (data) {

                        deferred.resolve(data);

                    },

                    error: function (err) {

                        deferred.reject(err);

                    }

                });

            },

            function (err) {

                deferred.reject(err);

            }

         );

 

        return deferred.promise();

    },

 

    getFileBuffer = function (file) {

        //See previous code

    };

 

    return {

        upload: upload

    };

 

}();

Again, once you have the library, uploading the file is easy. Here is the REST version of the upload method.

WingtipToys.Rest.Libs.upload("/apps/LibraryOperations/REST%20Documents", filename, file)

.then(

    function (data) {

        alert("Uploaded successfully");

    },

    function (err) {

        alert(JSON.stringify(err));

    }

);

Conclusions

As with most things in programming, it’s pretty simple when someone shows you how. Sparse documentation on MSDN, however, gave me a couple of days of agony. (I'll probably get a laugh when a reader points me at a full code sample somehere on MSDN that I missed.)

The big takeaway from this effort is that now you can easily use the REST interface for file uploading, which is good for files up to 2GB. Because CSOM is limited to 1.5MB, it seems like there is really little point to using anything but REST.

 
Posted by Scot Hillier on 26-Mar-13
0 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
 

Links to this post

Comments