Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Changes To The Dropbox API - Upload Files The New Way From a Windows Phone

DZone's Guide to

Changes To The Dropbox API - Upload Files The New Way From a Windows Phone

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

As I was working on my next Windows Phone project, I had to once again integrate the Dropbox API with my application. I already had a wrapper done (remember the SevenDrops project?) and it works pretty well. However, I signed up for a new API key, since the application is of a different nature compared to SevenDrops itself. I copied the code in my new project, modified the keys and... none of my previous method worked. I started SevenDrops - it works. The new app - fails on every single test.

Apparently, the problem is simple - Dropbox changed the terms of their API use for new applications. With SevenDrops, I used the very simple token acquisition method - by passing user credentials in the request URL. At the time, it was supported and the application worked well. This mechanism has since been discontinued. But why SevenDrops still works? My assumption is because at the time when I registered the application the basic auth token acquisition mechanism was available, I am still able to use it to avoid breaking the application. With new apps, there is only the OAuth way - which, in my opinion, should always be the first choice anyway.

I quickly refactored the auth code and was able to give my app access to the Dropbox account. And once again I hit the wall - the file upload does not work with the new API key. Take a look at what I was doing before:

public static void Upload(string name, byte[] content, string token, string apiKey, string consumerSecret, string tokenSecret)
{
    Dictionary<string, string> parameters = new Dictionary<string, string>();

    parameters.Add("oauth_consumer_key", apiKey);
    parameters.Add("oauth_signature_method", "HMAC-SHA1");
    parameters.Add("oauth_timestamp", CurrentUNIXTimestamp.Get());
    parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", ""));
    parameters.Add("oauth_token", token);
    parameters.Add("oauth_version", "1.0");
    parameters.Add("file", name);

    string OAuthHeader = GetOAuthHeader(parameters, "POST", "https://api-content.dropbox.com/0/files/dropbox/SevenDrops", consumerSecret, tokenSecret);

    string boundary = Guid.NewGuid().ToString();
    string header = "--" + boundary;
    string footer = "--" + boundary + "--";

    HttpWebRequest uploadRequest = (HttpWebRequest)WebRequest.Create("https://api-content.dropbox.com/0/files/dropbox/SevenDrops");
    uploadRequest.ContentType = "multipart/form-data; boundary=" + boundary;
    uploadRequest.Method = "POST";
    uploadRequest.Headers["Authorization"] = OAuthHeader;

    StringBuilder headers = new StringBuilder();
    headers.AppendLine(header);
    headers.AppendLine("Content-Disposition: file; name=\"file\"; filename=\""+ name +"\"");
    headers.AppendLine("Content-Type: application/octet-stream");
    headers.AppendLine();
    headers.AppendLine(Encoding.GetEncoding("iso-8859-1").GetString(content, 0, content.Length));
    headers.AppendLine(footer);

    byte[] contents = Encoding.GetEncoding("iso-8859-1").GetBytes(headers.ToString());

    object[] data = new object[2] { uploadRequest, contents };
    uploadRequest.BeginGetRequestStream(new AsyncCallback(GetData), data);
}

A lot of APIs that allow user content upload use a similar approach - have a multipart/form-data POST request with binary content delimited from the headers. Although viable, this might overcomplicate things. Dropbox API now gives developers a chance to use the PUT method to upload files. It is much simpler, and is it turns out - cuts out a whole chunk of my code.

The above method was reduced to this:

public static void Upload(string name, byte[] content, string token, string apiKey, string consumerSecret, string tokenSecret)
        {
            Dictionary<string, string> parameters = new Dictionary<string, string>();

            parameters.Add("oauth_consumer_key", apiKey);
            parameters.Add("oauth_signature_method", "HMAC-SHA1");
            parameters.Add("oauth_timestamp", StringHelper.UNIXTimestamp);
            parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", ""));
            parameters.Add("oauth_token", token);
            parameters.Add("oauth_version", "1.0");

            string workUri = "https://api-content.dropbox.com/1/files_put/dropbox/SevenDrops/" + StringHelper.EncodeToUpper(name).Replace("(","").Replace(")","");
            string OAuthHeader = OAuthClient.GetOAuthHeader(parameters, "PUT", workUri, consumerSecret, tokenSecret);

            HttpWebRequest uploadRequest = (HttpWebRequest)WebRequest.Create(workUri);
            uploadRequest.Method = "PUT";
            uploadRequest.Headers["Authorization"] = OAuthHeader;

            object[] data = new object[2] { uploadRequest, content };
            uploadRequest.BeginGetRequestStream(new AsyncCallback(GetData), data);
        }

This is a much cleaner way to upload files - you don't have to deal with the file processing as much as you did before. Notice how the URL is formed based on the file name directly - you no longer pass a file parameter.

TIP: Make sure you are using proper URL-encoded file names. Otherwise the request will fail.

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}