2012年3月17日土曜日

DropboxにRESTfulアクセス

少しDropboxにデータを送り込みたいとなって
エンジンをGoogleAppEngineで送り込む事になったので少し調べました。

SDKはiOS,Android(Java),Ruby,Pythonがあります。
今回はJavaを選択したので見ましたがHttp「s」URLConnectionを使用しており、
これがAppEngineでサポートしてないので実行環境でエラーが起こります。

ってことでRESTfulアクセスで行なってみました。

準備
まずここからアプリを登録してクライアントキーを作成します。
作成するとこんな感じ。
AccessTypeは後から変えられません。 「App folder」は専用のディレクトリのみアクセス可能なもの。 「Full Dropbox」はすべてのファイルアクセスです。 専用のディレクトリのみでOKだったんですけど、Fullにしました。
リクエストトークンの取得
https://api.dropbox.com/1/oauth/request_tokenにアクセスを行います。
URL url = new URL("https://api.dropbox.com/1/oauth/request_token");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoInput(true);
connection.addRequestProperty("Authorization", buildOAuthHeader(false));
connection.setRequestMethod("GET");
InputStream inStream = connection.getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(inStream));
String line = input.readLine();
view raw gistfile1.java hosted with ❤ by GitHub
で戻り値(line)は「oauth_token_secret=biv7oeyp3nvkb1g&oauth_token=ql0l5qpk8h10rjz」みたいな感じです。 buildOAuthHeader()はSDKのコードにもありますが、
  1. private static String buildOAuthHeader(boolean accessToken) {  
  2.     Map<String,String> map = new HashMap<String,String>();  
  3.     map.put("oauth_version","1.0");  
  4.     map.put("oauth_signature_method","PLAINTEXT");  
  5.     map.put("oauth_consumer_key",APP_KEY);  
  6.   
  7.     String sig = null;  
  8.     if ( accessToken ) {  
  9.         map.put("oauth_token",TOKEN_KEY);  
  10.         sig = encode(APP_SECRET) + "&" + encode(TOKEN_SECRET);  
  11.     } else {  
  12.         sig = encode(APP_SECRET) + "&";  
  13.     }  
  14.     map.put("oauth_signature",sig);  
  15.   
  16.     StringBuilder buf = new StringBuilder();  
  17.     buf.append("OAuth ");  
  18.   
  19.     Iterator<Entry<String, String>> itr = map.entrySet().iterator();  
  20.     while ( itr.hasNext() ) {  
  21.         Entry<String, String> entry = itr.next();  
  22.         buf.append(entry.getKey() + "=" + entry.getValue() + ",");  
  23.     }  
  24.     String data = buf.toString();  
  25.     return data.substring(0,data.length()-1);  
  26. }  
といった感じでヘッダに設定してあげてます。
認可用のURL作成
lineにある「oauth_token」の値を使います。
  1. System.out.println("https://www.dropbox.com/1/oauth/authorize?oauth_token=" + argsMap.get("oauth_token"));  
  2. System.in.read();  
read()はブラウザからの入力待ちです。 ブラウザを立ち上げるとDropboxに認証してその後許可のアクションがあります。
このアクセスの際に「oauth_callback」を指定しておくとコールバックしてくれます。 操作後の挙動としては ・許可(あり):コールバックURLに戻る ・許可(なし):許可画面に遷移 ・拒否:Dropboxのhomeに遷移 って感じです。 ここで良くOAuthでの認可コードを取るようなコードを書いてましたが、 コールバックを指定したとしてもそれはDropboxにはありません。 ※ただしコールバックにはuidとoauth_tokenを付けてくれるのでユーザ特定は可能です。 ようは「認可」を行った時点でアクセストークンは取れる状態になるってわけです。
アクセストークンの取得
認可されていれば以下ができます。
  1. TOKEN_KEY    = argsMap.get("oauth_token");  
  2. TOKEN_SECRET = argsMap.get("oauth_token_secret");  
  3. url = new URL("https://api.dropbox.com/1/oauth/access_token");  
  4. connection = (HttpURLConnection)url.openConnection();  
  5. connection.setDoInput(true);  
  6. connection.addRequestProperty("Authorization", buildOAuthHeader(true));  
  7. connection.setRequestMethod("GET");  
  8.   
  9. inStream = connection.getInputStream();  
  10. input = new BufferedReader(new InputStreamReader(inStream));  
  11.   
  12. line = input.readLine();  
リクエストトークンで取ってきていた値を設定してbuildOAuthHeader()の値をtrueの方で動かします。 アクセストークンもリクエスト時と同じように 「oauth_token_secret=biv7oeyp3nvkb1g&oauth_token=ql0l5qpk8h10rjz」 という値で入ってきます。 これでアクセス可能になります。
ファイルのアップロード
使用するのはファイルのアップロードだけだったので PUTでbodyに設定してあげてアップします。
  1. public static void put(String name,byte[] data) throws Exception {  
  2.   
  3.     URL url = new URL("https://api-content.dropbox.com/1/files_put/dropbox/" + name);  
  4.     HttpURLConnection connection = (HttpURLConnection)url.openConnection();  
  5.     connection.setDoInput(true);  
  6.     connection.setDoOutput(true);  
  7.     connection.addRequestProperty("Authorization", buildOAuthHeader(true));  
  8.     connection.setRequestMethod("PUT");  
  9.   
  10.     OutputStream outputStream = connection.getOutputStream();  
  11.     outputStream.write(data);  
  12.     outputStream.close();  
  13.   
  14.     InputStream inStream = connection.getInputStream();  
  15.     BufferedReader input = new BufferedReader(new InputStreamReader(inStream));  
  16.     String line = "";  
  17.     while ((line = input.readLine()) != null) {  
  18.         System.out.println(line);  
  19.     }  
  20. }  
ここでURLにある「dropbox」です。 「Full Dropbox」を選択した場合はこれでOKなのですが、 「App folder」の場合は「sandbox」を指定してあげます。 前者はフルなのでユーザのディレクトリ通りに設定していったりするのですが、 sandboxの場合、「apps/[アプリID]」が使用されて、そこのアクセスのみが可能です。 フルアクセスのViewerアプリを作らない限り、後者を選択した方が良いでしょうね。 ※ブログ書き始めてアプリを変更しました。
感想
DropboxのAPIって使うとは思ってなかったのでアクセスするとは思いませんでした。 まぁ本家が作っているクライアントが同期とかもかなり優秀なので あまりアクセスする人はいないのかもしれませんね。 ただファイルアクセスはかなり簡単にできたので、 クラウドから一時的に持ってくるツールは作りやすいかな?と感じました。 何点か気になるのは ・POSTアクセスのAPIがGETで取得できる ・PLAINTEXTを使用している ・一度認可されていると許可とかが出ない って感じですかね? ちなみに自分以外に公開するには 「My Apps」のところで「Additional users」をONにする必要があります。 製品バージョンと特に制限はないみたいに見えます。 ※開発中は5人だけっぽい感じには書いてありますけど。

2 件のコメント:

Unknown さんのコメント...

full でない場合、
フォルダを共有できないのでシステム的に使う場合少し注意が必要かも(^_^;)

Unknown さんのコメント...

HMAC-SHA1で行なってみました。
アプリの性質上、ファイルの名称に
「Data(1000)-2012110100000000.zip」みたいなファイル名を指定していたのですが、
シグネチャ生成時にURLをエンコードする部分で認証エラーが起こる見たいです。

もう少し見てみますが、一旦はURLのカッコを外してシグネチャエラーが治りました。