Uploading Files Using the REST API and Client Side Techniques
Over the past few months I have, on several occasions, been asked how to upload a file to SharePoint using nothing more than JavaScript, HTML and the REST API. A few weeks ago, on a customer project, I was asked to do exactly that and was astonished to find that there is sparse documentation and even fewer examples out there on the Internet. For this reason, I have decided to write a post on how to use HTML 5 and JavaScript to upload files to a SharePoint 2013 document library.
Please Note: This technique is only supported in browsers that support the FileReader object of the File API. Currently this includes, but is not limited to, Internet Explorer version 10, Firefox version 3.6 and Chrome version 7 (information about the File API, including the FileReader object can be found here).
There are two specific areas that I will cover in this article; how to extract the file from the user’s machine and how to post this file off to the correct document library (including how to upload into a folder).Please note that this article does not cover the authentication aspect of uploading files over REST. If you are performing the upload from a SharePoint hosted app then this is taken care of by the app model. If you are performing an upload from another source then you will need to include an OAuth token (either as an App or as a user) with the request by setting an additional header (“authorization”).
Getting the file
Traditionally, uploading files is achieved by using a form that POSTs off to a web server which then handles the upload. This means that developers do not have to directly reach into the client’s file system to obtain the file. However this also means that you need to have access to a web server where you can handle the upload. In some situations, SharePoint hosted apps for example, the developer does not have access to this infrastructure and the upload must be handled inside the client’s web browser.
Advancements in the JavaScript File API allow developers to access the local file system using a standard input tag. The magical part is that you can access the file contents without having to perform a post back. The JavaScript code below shows how you can read the contents of a file (assuming that you have an input tag of type file inside the HTML document with an ID of fileSelectorInput).
- var fileInput = $('#fileSelectorInput')[0];
- var reader = new FileReader();
- reader.onload = function (result) {
- var fileData = '';
- var byteArray = new Uint8Array(result.target.result)
- for (var i = 0; i < byteArray.byteLength; i++) {
- fileData += String.fromCharCode(byteArray[i])
- }
- };
- reader.readAsArrayBuffer(fileInput.files[0]);
The example first uses jQuery to obtain a reference to the input tag on the page. A new FileReader object is then created and the readAsArrayBuffer function is called on the FileReader passing in a reference to the first file in the list.
The onload function contains the contents of the file (inside result.target.result) however this is in the format of an ArrayBuffer. To upload the file to SharePoint this must be converted into a string which can then be set as the body of a POST operation. This can be done by iterating through the array and building up a string using the string.fromCharCode() function. Details of this encoding method can be found in this MSDN article.
Performing the upload
Performing the actual upload is relatively simple. In the example code at the end of this article I will be using the cross domain library provided with SharePoint 2013 to perform uploads from within a SharePoint Hosted App. This library allows for cross domain calls from pages in the App web to the parent site. I will also show the endpoints required to upload files without the cross domain library.
Uploading to the root folder of a document library (without the cross domain library)
This is probably the simplest endpoint that I will cover. TargetSite needs to be replaced with the URL of the SharePoint site (e.g. https://sharepoint.com/sites/site). Additionally, the final two parameters (TargetLibrary and TargetFileName) need to be replaced with the appropriate values. The URL is:
TargetSite/_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/Files/add(url=@TargetFileName,overwrite='true')?@TargetLibrary='LibraryName'&@TargetFileName='Target File Name'
Uploading to the root folder of a document library (with the cross domain library)
If you would like to perform the upload from a SharePoint hosted App you will need to use the cross domain library to overcome cross domain issues. This can be done by modifying the start of the URL and adding an additional parameter (TargetSite) which needs to be replaced with the URL of you target site (e.g. https://sharepoint.com/sites/site). You will also need to replace the AppWebUrl section with the URL of your AppWeb. All other parameters should stay the same. The URL is:
AppWebUrl/_api/SP.AppContextSite(@TargetSite)/web/lists/getByTitle(@TargetLibrary)/RootFolder/Files/add(url=@TargetFileName,overwrite='true')?@TargetSite=’Target site URL’&@TargetLibrary=’LibraryName’&@TargetFileName=’Target File Name’
Uploading to a folder within a document (without the cross domain library)
Uploading to a folder in a document library is fairly similar to uploading to the root folder. Again I will show two examples (one with and one without the cross domain library). The simplified version (without the cross domain library) can be performed using the URL below. You will need to replace the various components in the same way as you would for uploading to the root folder (mentioned previously). Also note the addition of a TargetFolder parameter and the change to URL to include the folder. The URL is:
TargetSite/_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/folders(@TargetFolderName)/files/add(url=@TargetFileName,overwrite='true')?@TargetLibrary=’LibraryName’&@TargetFolder=’Target Folder Name’&@TargetFileName=’Target File Name’
Uploading to a folder within a document (with the cross domain library)
You can also use the cross domain library to upload files to a folder within a document library. The URL needs to be modified slightly (in the same way as before) to redirect the message to the AppWeb and to include the TargetSite parameter. The URL is:
AppWebUrl/_api/SP.AppContextSite(@TargetSite)/web/lists/getByTitle(@TargetLibrary)/RootFolder/folders(@TargetFolderName)/files/add(url=@TargetFileName,overwrite='true')?@TargetSite='Target site url’&@TargetLibrary=’LibraryName’&@TargetFolder=’Target Folder Name’&@TargetFileName=’Target File Name’
Required Headers/Options
In addition to ensuring you use the correct endpoint and encoding, it is important to ensure that the correct headers/options are set on the request. The actual request should be sent as a POST operation and the content type, headers, content length and binary string request body options must be set.
Accept: The Accept option must be set equal to “application/json; odata=verbose” inside the headers of the request.
Digest Value: The digest value should be set equal to the digest value obtained from SharePoint. This should also be set in the headers section of the request. The digest value can be read from SharePoint pages or be requested from the app web. More information about digest values can be found here under the heading “Writing data by using the REST interface”.
Content Type: The content type of “application/json; odata=verbose” should be set on the request
Body: The body should be set equal to the file contents encoded as per the description in the MSDN article here.
BinaryStringRequestBody: The option “binaryStringRequestBody” should be set equal to true. This informs SharePoint that the body of the message contains binary data.
Content Length: The content length of the request should be set equal to the length of the body. Note: if you are using the request executor then the content length is optional, if you are uploading directly (without the request executor) then you must set the content length otherwise SharePoint will reject the request.
Conclusion
In summary, performing uploads via the REST API using only client side technologies is not only possible, but is actually quite simple. We have looked at how to extract the file from the user’s machine, generate the correct endpoint and set the correct headers/options on the request. When you put all of this together you can upload binary files to SharePoint from the end users web browser without having to process the files server side. This means that in situations such as SharePoint hosted Apps where developers do not have access to server side code uploads can still be performed. Furthermore in situations where you do have access to server side code you can still use the endpoints mentioned to upload files using the REST API.
The source code below contains a working example (in the form of a SharePoint hosted App).
Source Code
The following downloadable source code contains a working example of uploading files over REST. The sample is contained within a SharePoint hosted app and you will need to have Visual Studio 2012 installed to be able to run it. To run the example you will need to configure the SiteUrl of the project to point to a SharePoint 2013 developer site. To do this click on the project in solution explorer and open up the properties task pane. Enter the URL of your site into the SiteUrl box and then press F5.
Comments
Anonymous
April 22, 2013
Excellent post and something I have been wondering if this could be done in JS - even if using the WebServices API... I'll have to try and see if I can also get it done with WebServices. Once i get the file into base64 string, I should be able to do it. I have created "similar" solutions for SP2007 and SP2010 which basically use the upload page inside a manipulated iframe... The user never leaves the page while the page is uploaded. I currently have it as a jQuery pluin in my SPWigets library - here: purtuga.github.io/SPWidgetsAnonymous
May 15, 2013
Hmm, the csproj looks well formed, but the UploadOverREST.csproj say "unsupported" for me in VS2010 and 2012.Anonymous
May 22, 2013
Can this be done in Sharepoint 2010?Anonymous
May 27, 2013
Excellent post... Thanks I am able to upload documents using this format. But I have a question : Can we upload document with metadata columns using this Rest api,javascript and html5?Anonymous
June 01, 2013
Great PostAnonymous
June 05, 2013
Great article, works like a charm. However, I am also interested in including an identifier metadata along with my document when uploading. Is this possible?Anonymous
July 02, 2013
Many thanks for a great example.Anonymous
August 25, 2013
Great Post! Any reason this cannot be implemented through NAPA? Shame IE 7+ don't support this.Anonymous
August 25, 2013
If somebody get's this working via NAPA and the Current Site's document Library root folder PLEASE SHARE the full source and code. Ideally, I'd like to have a function that can set meta data columns and do some validation (restrict file type, rename the file, check if a combination (name and metadata ) is already there before it uploads. This would be extremely valuable as we have many interfaces where end users are only upload a file with some Identification data.Anonymous
August 25, 2013
Interested in this too.. I made an attempt to get this code working on NAPA under a ClientWebPart.aspx page. Had to add a reference jQuery 1.8.2 and tweak some of the code (expected === and new in some places). the form comes up and it locates files. But I get "Oooooops... it looks like something went wrong uploading your file." from IE 10 and Chromebook. I confirmed it's getting a good targetsiteurl, but #__REQUESTDIGEST does not resolve to anything...not sure what this is.Anonymous
October 31, 2013
The comment has been removedAnonymous
November 04, 2013
hi, thanks for the post i am bit confused about where to add my office365 site url can you please tell me how to and where to add url to jquery file in the sample solution Thanks in Advance MaheshAnonymous
November 07, 2013
Great Post, thanks for the direction. Have you ran into uploading 0kb files before?... I'm trying to post to an image library, everything succeeds but when I check the file, it's 0Kb. Any ideas?Anonymous
December 01, 2013
hi, Would like to know whether a managed metadata column can be inserted into the doc lib along with the file [ .doc/.xls/.txt/.pdf .. etc ] from a remote system using REST API. help is appreciated. thanks PrasadAnonymous
January 09, 2014
Hi, Great post. I am having a problem when trying to upload large files, the script is blocking the page and nothing happens next. Do you have any idea on how to resolve this ? ThanksAnonymous
February 05, 2014
Hi, Pretty great post. If i need to upload a file to sub folder of a folder in the library. Can i add the URL of the folder in the FolderName property. Eg. I have folder 'A' in library 'L'. There is a folder B inside folder A. I need to upload file inside folder B.Anonymous
April 15, 2014
Thanks for your post! One thing I have found is Digest Value is managed by RequestExecutor so it has not to be set as said in msdn.microsoft.com/.../jj164022(v=office.15).aspx. In fact, if your code is run as a app part it fails, because $("#__REQUESTDIGEST") is undefined.Anonymous
June 29, 2014
Thak you so much.. it was really helpfulAnonymous
July 02, 2014
This was very helpful but I seem to have found a flaw. When attempting to upload video the file uploads but a 404 error is return back to the executor. I used the following template: AppWebUrl/_api/SP.AppContextSite(@TargetSite)/web/lists/getByTitle(@TargetLibrary)/RootFolder/Files/add(url=@TargetFileName,overwrite='true')?@TargetSite=’Target site URL’&@TargetLibrary=’LibraryName’&@TargetFileName=’Target File Name’Anonymous
July 12, 2014
Not able to upload file to office 365 working fine with my on-premise server. can you help me with that.Anonymous
July 22, 2014
Here it is working with Office 365 uploading an attachment to a list item using cross domain. It's virtually the same as this blog's solution except for the REST URI. Of course, substitute your own list name for what I have in here (DOH%20News%20Drafts). function processUpload(fileInput) { var reader = new FileReader(); reader.onload = function (result) { var fileName = '', fileData = ''; var byteArray = new Uint8Array(result.target.result) for (var i = 0; i < byteArray.byteLength; i++) { fileData += String.fromCharCode(byteArray[i]) } // once we have the file perform the actual upload fileName = fileInput.name; performUpload(fileName, fileData); }; reader.readAsArrayBuffer(fileInput); } function performUpload(fileName, fileData) { // see blogs.msdn.com/.../uploading-files-using-the-rest-api-and-client-side-techniques.aspx var restURL = appweburl + "/_api/SP.AppContextSite(@target)/web/lists/GetByTitle('DOH%20News%20Drafts')/items(" + itemID + ")/AttachmentFiles/add(FileName='" + fileName + "')" + "?@target='" + hostweburl + "'" ; // use the request executor (cross domain library) to perform the upload var reqExecutor = new SP.RequestExecutor(appweburl); reqExecutor.executeAsync({ url: restURL, method: "POST", headers: { "Accept": "application/json; odata=verbose", "X-RequestDigest": digest }, contentType: "application/json;odata=verbose", binaryStringRequestBody: true, body: fileData, success: function (x, y, z) { // alert("Success! Your file was uploaded to SharePoint."); }, error: function (x, y, z) { // alert("Oooooops... it looks like something went wrong uploading your file."); } }); }Anonymous
August 19, 2014
how would be my url if i want to post the Title name?Anonymous
August 25, 2014
Nice Post !!!Anonymous
September 24, 2014
Great post. Thank. Best solution on .net for uploading documents to SharePoint online using JavaScriptAnonymous
November 19, 2014
When I try to upload a text file this works fine. When uploading a somewhat larger PDF, it get's corrupted. Any idea what's going wrong?Anonymous
November 22, 2014
Hi , Can I build similar application,but browser is IE8. Can you give me some inputs on how to proceed. Thanks in advanceAnonymous
January 06, 2015
For IE8, it does not support the FileReader API, so I'm not sure how you'd use this. I used the built-in attachfile.aspx. Here is some code that works in a napa app. It's up to you to supply this function with the GUID of the list you are attaching to and the itemID of the list item you are attaching to. "readAllAttachments()" is what happens after the file has been uploaded. Separately, I have code that checks for support of the FileReader API, so you know when to do this. It's simply if(window.FileReader){use the method from this blog post}else{use this method from this comment} function fileAttachDialog(){ //must remove last part of appweburl for this to work. var lastPos = appweburl.lastIndexOf("/"); var attachURL = appweburl.substr(0,lastPos); var dialogURL = attachURL + '/_layouts/Attachfile.aspx?ListId={'+ dohNewsGUID +'}&ItemId=' + itemID; var options = SP.UI.$create_DialogOptions(); options.title = "Upload Attachment"; options.width = 600; options.height = 150; options.url = dialogURL; options.dialogReturnValueCallback = Function.createDelegate(null, CloseCallback); SP.UI.ModalDialog.showModalDialog(options); function CloseCallback(result, target) { if (result == SP.UI.DialogResult.OK) { readAllAttachments(); } } }Anonymous
March 04, 2015
The comment has been removedAnonymous
March 27, 2015
Nice work buddy - really appreciatedAnonymous
July 25, 2015
Excellent! I've been searching for some time trying to find out how to upload to a subfolder. Very simple! Thanks a lot!Anonymous
August 27, 2015
Excellent work.It really helps. Thanks for the postAnonymous
November 03, 2015
HI, THE FILE CANNOT BE UPLOADED WHILE REDIRECTING FROM THE DEFAULT.ASPX PAGE TO ANOTHER CUSTOM PAGE CREATED BY USER. THE UPLOADING FUCTIONALITY IS IN CUSTOM PAGE ERROR IS THE SITE CANNOT BE ACCEESED .Anonymous
March 05, 2016
I am trying to upload large files greater than 10MB(Ex: 40 MB) through SharePoint cross domain Rest API calls. I am able to upload files that are upto 10MB using the code below. But when it comes to files that are greater than 10 MB i am getting an error saying Out of memory. Could someone please help me with uploading large files through SharePoint Rest API. Thanks in advance Document upload Service call var documentUpload = function (librarytitle, foldername, filedata, requestType, success, failure) { var deferred = $q.defer(); var reader = new FileReader(); reader.onload = function (result) { var fileData = ''; var byteArray = new Uint8Array(result.target.result) for (var i = 0; i < byteArray.byteLength; i++) { fileData += String.fromCharCode(byteArray[i]) } if (requestType == "DocumentUpload") { var url = appweburl + "/_api/SP.AppContextSite(@TargetSite)/web/lists/getByTitle(@TargetLibrary)/RootFolder/files/add(url=@TargetFileName,overwrite='true')?" + "@TargetSite='" + hostweburl + "'" + "&@TargetLibrary='" + librarytitle + "'" + "&@TargetFileName='" + filename + "'"; } // use the request executor (cross domain library) to perform the upload var reqExecutor = new SP.RequestExecutor(appweburl); reqExecutor.executeAsync({ url: url, method: "POST", headers: { "Accept": "application/json; odata=verbose", "X-RequestDigest": $("#__REQUESTDIGEST").val() }, contentType: "application/json;odata=verbose", binaryStringRequestBody: true, body: fileData, success: function (data) { //alert("Success! Your file was successfully uploaded to ClientPortal."); var jObject = JSON.parse(data.body) deferred.resolve(data); }, error: function (x, y, z) { console.log(z); } }); }; reader.readAsArrayBuffer(filedata); return deferred.promise; };