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.
The first thing to set up is the control used for selecting files from the web page. This is a pretty simple use ofinput 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];
}
Once the file is selected, you have to read the bytes into your JavaScript code. This is accomplished using theFileReader object. This object accepts the file information for loading asynchronously. The onload and onerrorevents 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 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());
}
);
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.