Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

[WP7] Upload a file to Dropbox using ReactiveOAuth.WP7

As I was helping @lancewmccarthy on its last application, I ran across the need to upload a file to Dropbox. While there is already a few libraries for WP7 handling this task, I couldn’t get any of them to work as expected: either they missed the file upload feature, or they used an older version of the API that new applications can’t use anymore. As such, I had to dive into the Dropbox’s OAuth API to write my own upload code. It was far from being painless, so I think the whole process is worth sharing.

First, what is OAuth? I won’t give much details as I discovered that recently, but basically it’s a protocol used to authenticate yourself and send orders to an http-based API. On the surface, it’s just about calling the appropriate webpage. But to prevent some kinds of attacks, OAuth forces you to add a few parameters to the uri, like a random value and a dynamically generated signature. It may be just me, but I think the official documentation isn’t precise enough on how to generate the signature, so I quickly gave up and searched for general-purpose OAuth libraries. The one I found and used is ReactiveOAuth.WP7. It was originally designed for Twitter, but it can also be used for Dropbox, with a few changes.

The documentation for Dropbox REST API can be found here. To use it, you must first register your application on the website, to get a unique code that you will use every time you call a Dropbox function. The application registration is done on this page and is almost instant. Note that your application is first registered with ‘testing’ status. It means you can only use your own Dropbox account with it. To remove this limitation, you have to apply for production status, which requires a validation from Dropbox administrators. You just have to fill a description for your app and explain why you need to use Dropbox, then you should receive a confirmation e-mail after a few hours. Anyway, on the application page you’ll find two codes: “App key” and “App secret”. We need these to use the API.


Now, let’s start Visual Studio and create a new WP7 project. Add the ReactiveOAuth.WP7 project to your solution. The whole project, not just the binaries, as we’ll have to make a few changes in the code.

In your main project, add a reference to the ReactiveOAuth project. Also add a reference to the Microsoft.Phone.Reactive and System.Observable assemblies.


The OAuth authentication and authorization process works as follows: first call the “request_token” page, giving your app key and secret (the two values that were provided on the Dropbox application page). The server answers with a token which will identify your session. Then, the user must allow your application to use his Dropbox account. For security reasons, you cannot automate this part, it requires user interaction. You’ll have to redirect the user to a specific webpage, where he can type his credentials and explicitly allow your application. If the user do theses steps correctly, then a call to the “access_token” page will return a token that you can use to call any API function you want.

For our tests, we need to upload something to Dropbox. For that purpose, we’ll create a test file when the page is loaded, and store it in the isolated storage. Therefore, we create a “CreateDummyFile” function and call it in the “OnNavigatedTo” event:

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   2: {
   3:     base.OnNavigatedTo(e);
   5:     this.CreateDummyFile();
   6: }
   8: private void CreateDummyFile()
   9: {
  10:     using (var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
  11:     {
  12:         if (isolatedStorage.FileExists("test.txt"))
  13:         {
  14:             isolatedStorage.DeleteFile("test.txt");
  15:         }
  17:         using (var stream = isolatedStorage.CreateFile("test.txt"))
  18:         {
  19:             var data = Encoding.UTF8.GetBytes("Hello world! - " + DateTime.Now.ToLongTimeString());
  21:             stream.Write(data, 0, data.Length);
  22:         }
  23:     }
  24: }

Then we use the “OAuthAutorizer” class to request a token. “Key” and “Secret” are constants added to the page, containing respectively the app key and app secret.

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   2: {
   3:     base.OnNavigatedTo(e);
   5:     var authorizer = new OAuthAuthorizer(Key, Secret);
   7:     authorizer.GetRequestToken("");
   8: }

Once we have the token, we must proceed with the authorization. We need to display a webpage to the user, so we add a few UI elements in the XAML:

   1: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   2:     <StackPanel>
   3:         <phone:WebBrowser x:Name="WebBrowser" Height="700"  IsScriptEnabled="True"
   4:                       Visibility="Collapsed" 
   5:                           Navigating="WebBrowser_Navigating" />
   6:         <StackPanel x:Name="UploadingPanel">
   7:             <TextBlock Text="Uploading file..." Height="50" TextAlignment="Center" />
   8:             <ProgressBar x:Name="ProgressBar" Height="20" IsIndeterminate="True" Visibility="Visible"/>
   9:         </StackPanel>
  10:     </StackPanel>
  11: </Grid>

In the code, we use the callback of the “GetRequestToken” method to construct the right uri and navigate to this page. The “authorize” function accepts a “oauth_callback” parameter. This parameter tells Dropbox to redirect to a specific page when the authorization process is done. This way we can detect the redirection to know when the user has finished. So our “OnNavigatedTo” event now looks like:

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   2: {
   3:     base.OnNavigatedTo(e);
   5:     this.CreateDummyFile();
   7:     var authorizer = new OAuthAuthorizer(Key, Secret);
   9:     authorizer.GetRequestToken("")
  10:         .ObserveOnDispatcher()
  11:         .Subscribe(token =>
  12:         {
  13:             this.RequestToken = token.Token;
  14:             var url = authorizer.BuildAuthorizeUrl("", token.Token);
  16:             url += "&oauth_callback=http://dummywebsite/dummy";
  18:             this.WebBrowser.Navigate(new Uri(url));
  19:         });
  20: }

Note the call to “ObserveOnDispatcher”. It tells the Reactive extensions to call the callback on the dispatcher thread. It’s required as we need to interact with the UI, to tell the WebBrowser control which page it should display. Without “ObserveOnDispatcher”, we would have to call “Dispatcher.BeginInvoke” to avoid a cross-thread exception.

In the XAML code I’ve provided, the WebBrowser is hidden by default. Now we need to set it to visible, and detect the end of the authorization process to hide it back and request the access token. To do that, we’re going to use the “Navigating” event of the WebBrowser control:

   1: private void WebBrowser_Navigating(object sender, NavigatingEventArgs e)
   2: {
   3:     if (e.Uri.AbsolutePath == "/dummy")
   4:     {
   5:         // Authorization done, cancel the navigation, hide the browser control, and proceed.
   6:         e.Cancel = true;
   7:         this.WebBrowser.Visibility = Visibility.Collapsed;
   8:         this.UploadingPanel.Visibility = Visibility.Visible;
   9:         this.GetAccessToken();
  10:     }
  11:     else
  12:     {
  13:         this.WebBrowser.Visibility = Visibility.Visible;
  14:         this.UploadingPanel.Visibility = Visibility.Collapsed;
  15:     }
  16: }

The “GetAccessToken” method simply request the access token, store it in a property, and call the file uploading code:

   1: private void GetAccessToken()
   2: {
   3:     var authorizer = new OAuthAuthorizer(Key, Secret);
   4:     authorizer.GetAccessToken("", this.RequestToken, this.RequestToken.Secret)
   5:         .Subscribe(token =>
   6:         {
   7:             this.AccessToken = token.Token;
   9:             this.SendFile();
  10:         });
  11: }

Now comes the tricky part. Out of the box, the ReactiveOAuth library is limited to HTTP GET and POST requests. To upload the file, we’ll need to use HTTP PUT. Therefore, there’s two modifications to do in the library.

First, open the “MethodType.cs” file, and add the PUT method:

   1: using System;
   3: namespace Codeplex.OAuth
   4: {
   5:     /// <summary>WebRequest HttpMethodType</summary>
   6:     public enum MethodType
   7:     {
   8:         Get, Post, Put
   9:     }
  11:     public static class MethodTypeExtensions
  12:     {
  13:         /// <summary>convert to UPPERCASE string</summary>
  14:         public static string ToUpperString(this MethodType methodType)
  15:         {
  16:             switch (methodType)
  17:             {
  18:                 case MethodType.Get:
  19:                     return "GET";
  20:                 case MethodType.Post:
  21:                     return "POST";
  22:                 case MethodType.Put:
  23:                     return "PUT";
  24:                 default:
  25:                     throw new ArgumentException();
  26:             }
  27:         }
  28:     }
  29: }

Then open the “OAuthClient.cs” file and find the “CreateWebRequest” method. Set its visibility to “Public”, and change the first line from:

   1: var requestUrl = (MethodType == OAuth.MethodType.Get) ? Url + "?" + Parameters.ToQueryParameter() : Url;


   1: var requestUrl = (MethodType == OAuth.MethodType.Get || MethodType == OAuth.MethodType.Put) ? Url + "?" + Parameters.ToQueryParameter() : Url;

What are we doing here? The “files_put” API of Dropbox requires parameters to be provided as for a GET request, and the file data to be provided as POST data. ReactiveOAuth.WP7 doesn’t seem to be able to handle this hybrid query, so we’re bypassing it and exposing the internal WebRequest object to do the upload ourselves. Basically, we only use ReactiveOAuth to create the request and fill all the parameters required by the OAuth protocol, like the signature, then we take car of the uploading.

Now we can write our “SendFile” method, with the necessary upload code:

   1: private void SendFile()
   2: {
   3:     var client = new OAuthClient(Key, Secret, this.AccessToken);
   5:     client.Url = "";
   7:     client.Parameters.Add("overwrite", "true");
   9:     client.MethodType = MethodType.Put;
  11:     var webRequest = client.CreateWebRequest();
  13:     webRequest.BeginGetRequestStream(this.StartUpload, webRequest);
  14: }
  16: private void StartUpload(IAsyncResult asyncResult)
  17: {
  18:     var request = (HttpWebRequest)asyncResult.AsyncState;
  19:     var postStream = request.EndGetRequestStream(asyncResult);
  21:     using (var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
  22:     {
  23:         using (var stream = isolatedStorage.OpenFile("test.txt", FileMode.Open))
  24:         {
  25:             stream.CopyTo(postStream);
  27:             postStream.Close();
  28:         }
  29:     }
  31:     request.BeginGetResponse(this.EndUpload, request);
  32: }
  34: private void EndUpload(IAsyncResult asyncResult)
  35: {
  36:     var request = (HttpWebRequest)asyncResult.AsyncState;
  38:     try
  39:     {
  40:         var response = (HttpWebResponse)request.EndGetResponse(asyncResult);
  42:         response.Dispose();
  44:         this.Dispatcher.BeginInvoke(() =>
  45:         {
  46:             this.ProgressBar.Visibility = Visibility.Collapsed;
  47:             MessageBox.Show("Your file has been sucessfully uploaded to Dropbox!");
  48:         });
  49:     }
  50:     catch (Exception ex)
  51:     {
  52:         this.Dispatcher.BeginInvoke(() => MessageBox.Show("An error occured: " + ex.Message));
  53:     }
  54: }

The “StartUpload” method simply opens the file from the isolated storage, then copies the contents inside the web request. The “EndUpload” callback is called when the upload is finished. There, we display a confirmation message.

And we’re done! After executing this program, the Dropbox webpage should appear, asking for your credentials. Then, when you’re done allowing the app, the upload will begin and a message box will confirm you that the file has been uploaded.


Note: my code modification inside the ReactiveOAuth.WP7 library is really ugly, as I don’t even bother to use Reactive extensions. That’s because I didn’t really need Reactive extensions in first place, and any ‘general purpose’ OAuth library would do the trick. Still, if you know an elegant way to use Dropbox’s ‘files_put’ function with ReactiveOAuth without modifying the library, please share it in the comments!

Publié dimanche 13 novembre 2011 15:14 par KooKiz
Classé sous : , , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :


Pas de commentaires
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- UWP or not UWP sur Visual Studio 2015 ? par Blog de Jérémy Jeanson le 03-08-2017, 19:12

- Désinstallation de .net Core RC1 Update 1 ou SDK de Core 1 Preview 2 par Blog de Jérémy Jeanson le 03-07-2017, 19:29

- Office 365: Ajouter un utilisateur ou groupe dans la liste des Site collection Administrator d’un site SharePoint Online via PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-24-2017, 18:52

- Office 365: Comment créer une document library qui utilise les ContentTypeHub avec PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-22-2017, 17:06

- [TFS] Supprimer en masse les dépendances à SQL Enterprise ou Developer avant de procéder à une migration par Blog de Jérémy Jeanson le 02-20-2017, 20:30

- Office 365: Attention au volume utilisé par les fichiers de Thèmes de SharePoint Online par Blog Technique de Romelard Fabrice le 02-07-2017, 18:19

- [SCVMM] Supprimer une machine bloquée par Blog de Jérémy Jeanson le 01-31-2017, 21:22

- Microsoft .Net Challenge 2017 par Le Blog (Vert) d'Arnaud JUND le 01-30-2017, 15:25

- Office 365: Utiliser le bouton Export to Excel depuis un teamsite SharePoint Online et avec le client Office 2007 par Blog Technique de Romelard Fabrice le 01-27-2017, 18:58

- Office 365: Forcer la réindexation des données dans une liste SharePoint Online par Blog Technique de Romelard Fabrice le 01-25-2017, 15:57