Wednesday, September 28th, 2011

Using the Box API with Javascript

By

Setup

In this tutorial, we’ll be using a local server to develop our app. You’re certainly welcome to use your own web server if you have one, but if not, grab a copy of XAMPP to host your app on your own computer.
In any event, you’ll want to create a new directory for your application. In XAMPP’s htdocs directory, we’ve created a new folder called “demo” where we’ll be developing our app.
We’ll also be taking advantage of jQuery, so if unfamiliar with either, check out the respective documentation pages!

Registering your app

First, head on over to https://www.box.net/developers/services, log in, and select “Create New Application”. After creating your application, edit the app and supply a redirect URL. Because we’re developing locally, we can just use http://localhost/demo/index.html. While you’re here, also make note of your app’s API key; we’ll need that later.

Authentication

Now that we’re all set up, we can start developing our app! Before users can access our app, they’ll need to log in to their Box. To do so, we’ll need to redirect them to a secure login page hosted on box.net. First, we’ll need to get an authentication ticket from the Box API via the get_ticket method. All of Box’s API methods have the same base URL: https://www.box.net/api/1.0/rest, and actions and parameters are passed to this URL in the query string. The get_ticket action takes a single parameter — our API key — so the URL we need to hit looks like https://www.box.net/api/1.0/rest?action=get_ticket&api_key=YOUR_API_KEY_HERE.

Unless you’re lucky enough to be a Box developer like myself, you’ll be hosting your app on a domain other than box.net (or a subdomain thereof). As a result, the Javascript Same Origin Policy is going to prevent us from making Ajax requests to the API from another domain (like localhost, which we’ll be using in this tutorial). As a result, we’re going to use YQL, a free service created by the Yahoo Developer Network, to make our API calls. The format of a YQL request is simple: you supply a URL and a format (like XML or JSON), and YQL will return the data you wanted from your cross-domain request.
We’ll also need our API key in every request we make, so it’s a good idea to save that in a Javascript variable like window.api_key. Once we receive a response from the Box API containing a ticket, we need to redirect the user to Box’s secure login page, using that ticket. So, a get_ticket request may look like this (notice how we’re redirecting the user to Box after we receive a ticket):

$.getJSON("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%20%3D%20'https%3A%2F%2Fwww.box.net%2Fapi%2F1.0%2Frest%3Faction%3Dget_ticket%26api_key%3D" + window.api_key + "'&format=json&diagnostics=true",
	function(response) {
		window.ticket = response.query.results.response.ticket;
		window.location.href = 'https://m.box.net/api/1.0/auth/' + ticket;
});

Notice how all parts of that URL are encoded; Javascript’s encodeURIComponent can come in handy here. After the user authenticates, they’ll be sent back to the page we specified when we set up our app. The URL will also contain an auth_token, which we’ll also need to include in all future API requests, so let’s save that in a variable like window.auth_token as well.

Getting Files and Folders

Phew! Now that a user has logged into Box, we can start interacting with their account. Among the most useful API methods is get_account_tree, which gives us a list of the files and folders contained within a user’s account. As with all API requests, we’ll need to include both our API key and the user’s auth token. We also need to specify a folder_id, which will serve as the root of the tree. A user’s root folder (their top-level, or “All Files”) folder, always has an id of 0. Because many users take advantage of Box’s huge storage offerings, getting a user’s entire account with one API call is probably going tobe too slow to be usable. As a result, consider supplying onelevel as an optional parameter to get_account_tree; rather than recursively returning a user’s account, only one level of tree depth will be returned by the API. The API also compresses responses by default, so let’s disable that for debugging purposes with the optional nozip parameter. So, our final API call is going to look something like:

$.getJSON("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%20%3D%20'https%3A%2F%2Fwww.box.net%2Fapi%2F1.0%2Frest%3Faction%3Dget_account_tree%26auth_token%3D" + window.auth_token + "%26api_key%3D" + window.api_key + "%26folder_id%3D" + folder_id + "%26params[]%3Donelevel%26params[]%3Dnozip'&format=json&diagnostics=true", function(response) {});

Inside of our callback, the response variable contains the user’s folder information, wrapped in a few extra properties added by YQL. If, for example, we want to display a list of files and folders in a user’s account, we need to iterate over the folders.folder and files.file properties of response.query.results.response.tree.folder. Here’s what that could look like:

var ul = $('
'); var ul_html = ''; // iterate over folders and add to list if (response.query.results.response.tree.folder.folders) { var folders = response.query.results.response.tree.folder.folders.folder; for (var folder in folders) { ul_html += '
	' + folders[folder].name + '
'; } } // iterate over files and add to list if (response.query.results.response.tree.folder.files) { var files = response.query.results.response.tree.folder.files.file; for (var file in files) { ul_html += '
	' + files[file].file_name + '
'; } } // render listview ul.html(ul_html); ul.appendTo('body');

Notice that the API response contains both file/folder names and IDs. Each ID is unique throughout Box, and can be used to retrieve information about a single item in the user’s account. For example, if we wanted to get the tree of a folder inside the user’s root folder, we’d want to call get_account_tree again, this time supplying the folder’s ID for folder_id instead of 0 (which, remember, will always be the user’s root folder).

Downloading Files

Now that we can navigate within a user’s account, we’d like users to be able to download their files. The download API method makes this painless. Download URLs have a different format than the other API methods we’ve seen so far. To download a file, simply direct the user to a URL in the format https://www.box.net/api/1.0/download/<auth_token>/<file_id>. Here, auth_token is the same auth token we’ve been using throughout our app, and file_id is the unique, numerical ID of the file, which you can get from the get_account_tree method.

Uploading Files

Uploading files is as simple as sending a POST request to a URL similar to a download URL. To upload a file, POST a request to a URL in the format https://upload.box.net/api/1.0/upload/<auth token>/<folder id>.In this case, folder_id. For example, we could present the user with an HTML <form> with the attributes method=”POST” enctype=”multipart/form-data” action=”https://upload.box.net/api/1.0/upload/<auth token>/<folder id>” and containing an <input type=”file” />. However, simply submitting the form via an <input type=”submit” /> element will bring the user to an API URL (and therefore out of our app), which probably isn’t what we want. One option is to use a plugin like the jQuery Form Plugin.
Alternatively, we create and submit another form (in addition to the form in which the user selected a file to upload) hidden in an invisible iframe. That way, when we call the native form submit() method, we won’t be navigated away from the page. To do so, we simply need to construct an <iframe>, a <form> containing an <input type=”file” />, append the <form> to the <iframe>, and submit() the form. It’s important to note that the invisible <input> element we create must have the same content as the <input> we presented to the user; the easiest way to do this is to simply append another copy of the presented <input> to the <iframe>. Now, we’d like to know when our file has finished uploading. One way to do this is to continuously poll the <iframe>’s location; once it is no longer accessible, it must be pointing to URL on another server (which, in this case, is Box’s API). Putting all that together, we could use something like this (provided the input we presented to the user had a class of upload_file_input):

// get element representing file to be uploaded
var file_input = $('.upload_file_input');

// create iframe containing a form whose input matches that presented to the user
var iframe = $('');
var iframe_form = $('&lt;br /&gt;
&lt;form method="post" enctype="multipart/form-data" action="' + form.attr('action') + '"&gt;');
iframe_form.append(file_input);
$('body').append(iframe);
iframe.contents().find('body').append(iframe_form);
iframe_form.submit();

// poll the iframe to detect when the file has been uploaded
setInterval(function() {
	if (document.getElementById('upload_iframe').contentWindow.location.href == undefined)
	{
		alert('Upload successful!');
	}
}, 300);

Post by Thomas MacWilliam, Software Engineer

  • Carlos

    Hi,

    I am really interested in last piece of script and can’t seem to fully understand how to implement it.

    My scenario is:

    A form with a file upload field that should submit to Box.net to store the file and the secondary form on the same page is for your normal information that posts to a different server.

    I am trying to have the user upload the file but not receive the API XML response in the iframe but instead a blank screen so it doesn’t break the user experience.

    The last piece of script seems like it will do this but i am not understanding how to implement it. Do i need to create 2 iframes to handle the form and its copy?

    • Vishal

      Can someone please respond on this? I tried jqueryform plugin. It successfully uploaded the file to box.net but fails to parse the response and hence success callback function never calls.

      • Aparajita K

        Can you please post sample code how u implemented i need it. I want to upload files to box.net using javascipt sdk of box api. I am getting 200 ok but not able to retrieve ticket. Please respond me ASAP

  • Huang Potter

    Hi, in your article, you said we could use jQuery Form Plugin to upload file to box.net. I can’t figure it out how to do it. jQuery Form Plugin uses hidden iframe to get response from box.net. But due to cross-domain restriction, you can’t access the response. So not sure how you get around this limitation. Are you sure we can use jQuery Form plugin? Do you have some sample? Thanks

    • Damon

      Seven months later, we notice the same issue. You can’t access the response after a successful post. Our example javascript-only solution — using jQuery but no plugins — is as follows.

      var api_key = ‘awesomeapikey';
      var token = ‘tokengoodness';
      var folder_id = ‘6’;
      var upload_url = ‘https://upload.box.com/api/1.0/upload/’ + token + ‘/’ + folder_id;
      $.ajax(upload_url, {
      data: formObject,
      type: ‘POST’,
      cache: false,
      contentType: false,
      processData: false,
      success: function(resp_data, textStatus, jqXHR) {
      alert(resp_data);
      }
      });