Introduction
A few of my applications generate files locally and although methods to transfer those files like Bluetooth/Wifi transfer , email and similar are included users are heavily using the Cloud so the option to write the files to the Cloud was due pronto. We can accomplish this using the RESTful API/services from the Cloud provider.RESTFul services are becoming a predominant Web service nowadays due to its simpler style specially its stateless mode. Clients on different platforms can access resources via this architecture with very low complexity. Clients need to authenticate to the server and the OAuth is the method of choice since it is an open and straightforward standard with a decent level of security. In this article we will walk through an Android implementation of uploading a file to dropbox using their RESTFul API which utilizes OAuth authentication.
Download source code - 150.2 K
Background
Most of the big players like Google, Facebook, Tweeter, etc. offer RESTful services with OAuth authentication.The OAuth carries out the authentication with these few steps:
- Ask the server for permission to request the user to access his/her account. To carry out this the client (e.g. our code) needs a pair of key/secret_word credentials which is available from the provider upon request.
- Upon success we then ask the user for permission this usually requires for him/her to log in to the Dropbox account.
- Upon success a new set of key/secret_word credentials is provided and with this set handy all of the available APIs/methods can be accessed using this pair to sign each request, in our case we can upload files, read user info , etc.
The syntax is straightforward:
<URL from the RESTful API>?<OAuth args ...>&<Dropbox args...>
For instance to start the Dropbox OAuth we need to call the URL below without any DropBpx args just the regular OAuthhttps://api.dropbox.com/1/oauth/request_token?<OAuth args ..>
oauth_consumer_key=<key> provided by Dropbox
oauth_token=<secret_word> provided by Dropbox
oauth_nonce=<a number used once> a random string that is meant to uniquely identify each signed request.
oauth_timestamp=<when the request is sent>
oauth_signature_method=HMAC-SHA1 (it could be any other standard encryption used to generate the nonce)
oauth_version
The combination of nonce and timestamp takes the load off the server
and allows the Service Provider to only keep nonce values for a limited
time. The particular signature specification can be acquired and
implemented internally in our code but there are quite a few pre-cooked
libraries already so no need to reinvent the wheel here we will be using
the Signpost library to help us with the OAuth authentication steps.Finally RESTful providers like Dropbox also offer SDKs specific to the platforms (e.g. items for Android, iPhone, etc) but these are nothing but wrappers of the basic RESTful APIs implemented for the specific platform.
1. Implementing the code
The audience here is supposed to be well versed in Java/Android already so we will skip the basics and get to the meat of the topic. Our basic screen looks like this.1.1 Prerequisites: Manifest and Jar libraries
The Manifest needs to include a section that allows our App access to the net. Also our application needs an intent-filter so that our browser fires up properly and a definition of our dummy schema/URL used when we come back to our application from the browser.<activity android:name=".main"
....
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myrest" android:host="myStep.com" />
</intent-filter>
</activity>
...
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
We need to download the Signpost jar library and add it to our project ,we just drop it to the libs folder and add it Also we need to keep a reference to the Dropbox RESTful APIs.
1.2 Getting the credentials to sign an API request
Call the server properly to start up the process, the calls to the SingPost methods are highlightedprivate static CommonsHttpOAuthConsumer consumer;
private static CommonsHttpOAuthProvider provider ;
...
// need to request this
String DropB_key="<request this From DropBox>";
String DropB_secret="<request this From DropBox>";
// from dropB API
String request_url = "https://api.dropbox.com/1/oauth/request_token";
String authorization_url = "https://www.dropbox.com/1/oauth/authorize";
String access_url ="https://api.dropbox.com/1/oauth/access_token";
// to come back to our App after user authorizes
String callBack_url ="myRest://myStep.com";
...
// ask authorization to request the user
consumer = new CommonsHttpOAuthConsumer(
DropB_key, DropB_secret);
provider = new CommonsHttpOAuthProvider(
request_url, access_url, authorization_url);
// we r good not let's ask the user's permission
try
{
String authURL = provider.retrieveRequestToken(
consumer, callBack_url);
Intent intent2 = new Intent(Intent.ACTION_VIEW);
intent2.setData( Uri.parse(authURL) );
startActivity(intent2);
}catch (Exception e)
{}
As you can see the highlighted Signpost library
methods make it very straightforward to start out the authentication
process taking care of all
of the gory details. When we call the user for authorization we will
fire a browser to get back to our App the callBack_url is set to a dummy
schema and URL that we can
catch on the onResume event and wrap up the process. .. @Override
public void onResume()
{
// upon confirmation the call back will come to here.
super.onResume();
Uri uri = this.getIntent().getData();
...
// so it is coming back from the auth, catch it when it comes to our dummy URL
if( uri != null)
{
...
// now let's do the real stuff
if( uri.getHost().equals("myStep.com"))
{
//grab the tokens
String parms = uri.getEncodedQuery();
// perfomr the write thru
try {
// grab the token/secret items to carry on
String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
provider.retrieveAccessToken(consumer, verifier);
String ACCESS_KEY = consumer.getToken();
String ACCESS_SECRET = consumer.getTokenSecret();
Log.d("OAuth Dropbox", ACCESS_KEY);
Log.d("OAuth Dropbox", ACCESS_SECRET);
...
We have got the credentials to sign requests to the
available RESTful APIs, we are good to go now.1.3 Request user info and upload a file to Dropbox
With the credentials acquired the rest is straightforward, we get the proper API (a URL from Dropbox) and carry out either a GET or a POST as specified by the API, Dropbox returns either a few values formatted as URL arguments, or a JSON set.Each request must be signed with those credentials
We ask Dropbox for current user info below:
...
consumer.setTokenWithSecret(ACCESS_KEY, ACCESS_SECRET);
String uRL_file_list_req="https://api.dropbox.com/1/account/info";
HttpClient httpclient = new DefaultHttpClient();
HttpGet request = new HttpGet(uRL_file_list_req );
consumer.sign(request);
ResponseHandler<String> handler = new BasicResponseHandler();
try {
result = httpclient.execute(request, handler);
} catch (ClientProtocolException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Protocol failure uploading the file",
Toast.LENGTH_SHORT).show();
return;
} catch (IOException e) {
e.printStackTrace();
Log.e("******",Log.getStackTraceString(e));
Toast.makeText(getApplicationContext(), "IO failure uploading the file",
Toast.LENGTH_SHORT).show();
return;
}
JSONObject json_data = new JSONObject(result);
JSONArray nameArray = json_data.names();
JSONArray valArray = json_data.toJSONArray(nameArray);
...
We upload a text file with contents of the simple Android UI above,
changing the proper MIME type and encoding the contents properly
is all it is needed to upload any other file type like an image.//
// last but not least let's upload a file
// default parm is overwritten so we keep it simple
String textStr = myContents.getText() + "";
String fileName = fName.getText() + "";
uRL_file_list_req="https://api-content.dropbox.com/1/files_put/" +
"sandbox/" + fileName ; //+ "?param=val" ;
consumer.setTokenWithSecret(ACCESS_KEY, ACCESS_SECRET);
HttpPost request3 = new HttpPost(uRL_file_list_req );
request3.addHeader("Content-Type", "text/plain");
request3.setEntity(new StringEntity(textStr));
...
consumer.sign(request3);
result="";
try {
result = httpclient.execute(request3, handler);
} catch (ClientProtocolException e) {
e.printStackTrace();
Log.e("$$$$$$$",Log.getStackTraceString(e));
Toast.makeText(getApplicationContext(),
"Protocol failure uploading the file",
Toast.LENGTH_SHORT).show();
return;
} catch (IOException e)
{
e.printStackTrace();
Log.e("******",Log.getStackTraceString(e));
Toast.makeText(getApplicationContext(),
"IO failure uploading the file",
Toast.LENGTH_SHORT).show();
return;
}
// parse out the data
json_data = new JSONObject(result);
nameArray = json_data.names();
valArray = json_data.toJSONArray(nameArray);
Toast.makeText(getApplicationContext(), "Succes!! - " +
valArray.getString(7) + " " + valArray.getString(10),
Toast.LENGTH_SHORT).show();
httpclient.getConnectionManager().shutdown();
...
2. Caveats
The only issue here is that the Dropbox documentation reference is a bit blurry, when you need to carry file transfer operations the URL is of the formhttps://api-content.dropbox.com/1/files/<root>/<path>
<root> is a string with a value of either dropbox or sandbox , when you request the initial credential pair from Dropbox it defaults
you in Folder mode with root as sandbox. Later on you can request to change the status to production so users can use your App.
It will create a folder under Apps with the name of your application.<path> is the path to the actual file and it is relative to the App folder, e.g., to stat file test.txt sitting in the Apps/myCoolApp/ folder the above should be
https://api-content.dropbox.com/1/files/sandbox/text.txt
3. Final Thoughts
Very straightforward as you can see just a matter of getting through the specification of the Dropbox API. Having the credential keys handy and stored as a local preference in your app can allow further queries or file operations to Dropbox from your application without any other user interaction with Dropbox.The included sample code has the little extra details to get this working on Android since we are calling the RESTful API and not the SDK it is easy to port over to other platforms. Obviously the sample code is just a bare sample to get things going.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
About the Author
|
becker666
Software Developer (Senior)
BSC Inc
United States
Member
|