Integrating a Web app seamlessly with OneDrive – personal and business
Integrating with cloud drives can be very beneficial for the users of your applications, enabling them to save to locations that can automatically be shared with other users. Today there are two versions of OneDrive; personal and business. These are in fact fundamentally different, built on completely different engines. This means that at the core, there really are two different APIs to deal with and two different feature sets for what you can do. If you're integrating on a backend level, you have to use both APIs if you want to work with OneDrive today. But, if you happen to want to integrate from a frontend - Web - perspective, there are ways to ease the pain a bit. At least if you're looking for easier ways for just picking files for opening and picking folders for saving things. We have 2 versions of the File Picker API, Version 5.0 and 6.0. Version 6 is the one we want, it supports both.
File Pickers
The point of the picker abstraction is to give you an easier way to be able to pick files / folders and then also make it easy to actually download or upload the files. Also part of the abstraction is to make the underlying APIs hidden; which are different and make this as seamless as possible. This is true up to a certain point. Its when we want to save files that exist on the server or are being generated from the server we get into a bit of trouble. In the API you can either provide the file from a file picker that lets you select a file on the users hard-drive, or you can give the API a reference to a public URL that OneDrive can download the file from - typically the server and very useful if you're generating things on the server. Here comes the problem; OneDrive for business does not support this yet. In fact you get a clientside exception if you do this and there really is not an easy workaround for it, unless you create your own picker and deal with everything yourself. I'm not going to go indepth in how to set up OneDrive for Personal and/or Business. Only one note; in the line where you include the OneDrive client API into your Web page, you can in fact have two different client-ids registered; personal and business:
Also worth noting, the client-id attribute does not need to sit on the <script/> tag at all. In fact, it is looking for an element with the id "onedrive-js", so any element with that - in case you're using an AMD approach for loading things dynamically.
Changing the client library
A solution to this is to support one way for OneDrive personal to pull the file from you and another way to push the file to OneDrive for business, but try to maintain as little as possible overhead for the application to having to know about this. What I came up with is a slight alteration to the OneDrive JavaScript file. Of course; health warning must be applied at this point! This is not an officially, recommended Microsoft way to do things - just a way that works without having to wait for the feature to be implemented from the OneDrive team. The implication of this is obviously that you're on your own for maintenance with regards to new versions of the JavaScript file. But if you're in the position of not having time to write your own picker, you might consider this a worth while work around.
Health warning aside; lets get our hands dirty with how this can be worked around. First thing we're going to need is the debug version of the JavaScript client API. Easily append .debug to the regular file (before the extension) and you should find it: https://js.live.net/v6.0/OneDrive.debug.js.
In the file, locate the Saver.prototype._executeUrlUpload function declaration. In this we're going to introduce 4 lines of code. The purpose of the inserted code is to provide us with a callback mechanism for the scenario of OneDrive for Business and wanting to utilize the URL, rather than user files from the local harddrive.
The beauty of this particular function is that it comes in with the details about which folder was chosen and the actual accessToken, leaving us to do the things we need to do without having to authorize on the server.
Using it
Going back to the example on the OneDrive SDK page for the Save file picker, it refers to a "launchOneDriveSaver" function. We can now change this a little bit.
Implement the override function in the OneDrive API that we now support.
In this particular implementation, for simplicity - it uses the good old "Generic Handler" in ASP.NET. It passes along the folderId and the accessToken given from the OneDrive API. For this we're also using jQuery just for simplicity of implementation to post the information to the handler on the server. This is completely up to you how you'd do this - if you're in an Angular app you'd probably use the $http object for instance. Same for "Generic Handler" bit, you could easily use WebAPI or similar. Notice that the function also gets the options that sits inside the OneDrive object, basically the options we pass into the .save() function. Normally you might not keep these two things in the same file, and you'd need it to basically fire off the success or error callbacks properly. Obviously, this could also be done through Promises where the override function could return a promise and the internal implementation of the workaround could then call the callbacks passed from the options. But in the interest of not bringing in to many moving parts. Consider it a reference for showing the flow. (Enough excuses :) )
The server code that needs to upload this; UploadFile.ashx would then do the following:
If you look at the OneDrive Dev center, you'll notice that the server side way of upload files has a hard limit of 4MB files and no support for chunked uploads. The direct OneDrive for business API however supports uploading large files, so we use this directly using a WebClient. In order to get the URL correct we need to get this information from the access token. The token is JWT encoded and we therefor pull in a package for dealing with that. Once decoded, we can grab a claim that sits there called "aud", which is the tenant URL we need. Composing the URL then is a simple task. Only thing we then need is to add the access token as a bearer in the PUT request we will do for putting the file in place.
The entire solution can be found on GitHub here. Including the Generic Handler for the GetFile.ashx, which is used for the OneDrive for personal part of it.