How to update files to Google Drive by API.
Google Drive Android API
Getting Start から順次作業を進めます。
Get an Android certificate and register your application
アプリごとにGoogle Driveにアクセスするための認証しなさい、という事です。認証はアプリに付けられた署名のフィンガープリントによって行います。注意すべきは、デバッグ用の署名と本番用の署名は違う、ということです。ご存知のように、アプリをEclipse等から起動した場合、デバッグ用の署名が自動で付けられます。これは本番用に(Android Tool⇛署名付きアプリをエクスポート)アプリを作成する際に使用する署名とは違うものです。
ですので、Google Developers Consoleに登録する際は、デバッグ用と本番用の2つを登録してしまったほうが良いと思います。
また、フィンガープリントをGUIで見ることができるEclipseプラグインが有ります。
http://keytool.sourceforge.net/
Create a Proguard Exception
Proguardの設定をマニュアル通りに行っても、エラーが出てコンパイルできませんでした。他にも例外処理を加えなければならないとの情報もありましたが、結局Proguardをオフにしてコンパイルしました。
Creating files
ここで書かれているコードは、空のファイルを作成するだけで、ローカルファイルをアップロードする訳ではありません。uploadFile(.., .., .., URI)なんてメソッドがあればいいのですが・・・。この情報が見つからなくて苦労しました。SDKを使う昔の方法や、POSTする方法しか見つからず。こんな方法しかないの?というコードになってしまいました。
ContentsResultCallback
↓
fileCallback
↓
ApiClientAsyncTaskにDriveFileを渡す
↓
DriveFileをMODE_WRITE_ONLYで開いてOutputStreamで書き込む
また、Drive上のファイル名やフォルダ名でアクセスすることは、このAPIでは不可能です。なんと不便なのでしょう!DriveIdという任意の文字列を介してアクセスすることになります。
例)
カメラで撮った画像をそのままGoogle Driveの指定したフォルダにアップロードするコードです。
フォルダのDriveIdは、下記のサンプルを参考に、ユーザーが指定するようにしました。
https://github.com/googledrive/android-demos/blob/master/src/com/google/android/gms/drive/sample/demo/PickFolderWithOpenerActivity.java
DriveIdはPreferenceに保存しておきます。もしフォルダが見つからない場合は、ルートフォルダに保存します。
また、PrefUtilは私の作ったクラスで、Preferenceを読み込み/書き込みするだけなので、一般的なコードに書き換えれば動作します。
(1) Activity(implements Camera.PictureCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener)
googleApiClient.connect()が成功するとonConnected()に飛びます。
static GoogleApiClient googleApiClient; static String uploadFileName; static byte[] uploadData; static DriveId folderDriveId; @Override public void onPictureTaken(byte[] data, Camera camera) { try { String myPath = Environment.getExternalStorageDirectory().getPath() + "/SecEye"; File dir = new File(myPath); if (!dir.exists()){ dir.mkdirs(); } Calendar calendar = Calendar.getInstance(Locale.getDefault()); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MMdd_HHmm_ss", Locale.getDefault()); String myFileName = simpleDateFormat.format(calendar.getTime()) + ".jpg"; String myFile = myPath + "/" + myFileName; BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(myFile)); outputStream.write(data); outputStream.close(); Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri contentUri = Uri.fromFile(new File(myFile)); mediaScanIntent.setData(contentUri); this.sendBroadcast(mediaScanIntent); uploadFileName = myFileName; uploadData = data; googleApiClient = new GoogleApiClient.Builder(this) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); googleApiClient.connect(); } catch (Exception e) { Log.i("onPictureTaken", "file_error"); } finally { Log.i("onPictureTaken", "End"); finish(); } }(2) API関係
onConnected()で指定したフォルダを探しに行きます。ただし、DriveIdが空欄だと落ちるので、その場合はルートフォルダにファイルを作成する道をとります。newContentsのCallbackを指定フォルダ用とルートフォルダ用に分けました。
フォルダが無事見つかった場合は、フォルダのDriveIdを利用してDriveFolderを作成し、そこにファイルを作成します。
ファイルが指定フォルダ又はルートフォルダに作成されると、fileCallbackへ進みます。
// Google Drive Upload Tools @Override public void onConnectionFailed(ConnectionResult arg0) { finish(); } @Override public void onConnected(Bundle connectionHint) { PreferenceUtil prefUtil = new PreferenceUtil(this); if (!prefUtil.GetString("folderDriveId").isEmpty()) { Drive.DriveApi.fetchDriveId(googleApiClient, prefUtil.GetString("folderDriveId")) .setResultCallback(idCallback); } else { Drive.DriveApi.newContents(googleApiClient) .setResultCallback(contentsOnRootCallback); } } @Override public void onConnectionSuspended(int cause) { //finish(); } final private ResultCallback<DriveIdResult> idCallback = new ResultCallback<DriveIdResult>() { @Override public void onResult(DriveIdResult result) { if (!result.getStatus().isSuccess()) { Drive.DriveApi.newContents(googleApiClient) .setResultCallback(contentsOnRootCallback); } else { folderDriveId = result.getDriveId(); Drive.DriveApi.newContents(googleApiClient) .setResultCallback(contentsCallback); } } }; // create in selected folder final private ResultCallback<ContentsResult> contentsCallback = new ResultCallback<ContentsResult>() { @Override public void onResult(ContentsResult result) { if (result.getStatus().isSuccess()) { MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setTitle(uploadFileName) .setMimeType("image/jpg") .setStarred(false).build(); DriveFolder folder = Drive.DriveApi.getFolder(googleApiClient, folderDriveId); folder.createFile(googleApiClient, changeSet, result.getContents()) .setResultCallback(fileCallback); } } }; // create in root folder if (unknown folder/not selected) final private ResultCallback<ContentsResult> contentsOnRootCallback = new ResultCallback<ContentsResult>() { @Override public void onResult(ContentsResult result) { if (result.getStatus().isSuccess()) { MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setTitle(uploadFileName) .setMimeType("image/jpg") .setStarred(false).build(); Drive.DriveApi.getRootFolder(googleApiClient).createFile(googleApiClient, changeSet, result.getContents()) .setResultCallback(fileCallback); } } }; // two contentsCallbacks return one fileCallback final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() { @Override public void onResult(DriveFileResult result) { if (result.getStatus().isSuccess()) { DriveFile file = result.getDriveFile(); new EditContentsAsyncTask(CameraActivity.this).execute(file); } } };(3) アップロード
作成した空のファイルを開き、OutputStreamを使って画像データをアップロードします。アップロードが終わったらGoogleApiClientをdisconnectしていますが、このタイミングは設計によりけりです。
public class EditContentsAsyncTask extends ApiClientAsyncTask<DriveFile, Void, Boolean> { public EditContentsAsyncTask(Context context) { super(context); } @Override protected Boolean doInBackgroundConnected(DriveFile... args) { DriveFile file = args[0]; try { ContentsResult contentsResult = file.openContents( getGoogleApiClient(), DriveFile.MODE_WRITE_ONLY, null).await(); if (!contentsResult.getStatus().isSuccess()) { return false; } OutputStream outputStream = contentsResult.getContents().getOutputStream(); outputStream.write(uploadData); com.google.android.gms.common.api.Status status = file.commitAndCloseContents( getGoogleApiClient(), contentsResult.getContents()).await(); try { if (googleApiClient.isConnected() || googleApiClient.isConnecting()) { googleApiClient.disconnect(); } } catch (Exception e) { } finally { googleApiClient = null; } return status.getStatus().isSuccess(); } catch (IOException e) { } return false; } @Override protected void onPostExecute(Boolean result) {} }
0 件のコメント:
コメントを投稿