Google Drive API使ってアップロードから共有リンククリップボードコピーまでやってくれる便利ツールつくった

## はじめに
今勤めてる会社でGoogleDriveのお世話になってるんですが、アップロードが結構地味に手間で。
手順的には

1. 資源を用意
1. 適当な場所でZIP圧縮
1. ブラウザでGoogleDriveのページを開く
1. 一時保存置き場にしているフォルダを開く
1. ZIPをD&Dでアップロード
1. アップロード完了したらファイルに対してドメイン共有を有効にする
1. 共有リンクをコピー

と、こんなかんじ... まぁめんどくさいこと。
ということで２〜７をまるっとボタン１発でできないか考え、アプリをつくってみました。

そんなに難しいことはしてないんですが、ちょこちょこハマったところがあるので（あとC#での事例も少なそうだったので）ポイントを共有しておきます。

## つくったもの
リポジトリはこちら
https://github.com/ambleside138/GoogleDriveSender

こんなかんじで動かします
＃キャプチャはプライベートの個人アカウントで動かしたものです
![sender_progress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77732/7a1030e8-97a9-1098-1d50-0ed67d5f54de.gif)

1. アップロードしたいファイル/フォルダを右クリック
1. 送るメニューからアプリ（GoogleDriveSender）を選択
1. アップロード処理が完了するとクリップボードにリンクURLがコピーされている

という流れ。だいぶ手数が減りました！

## アプリ作成のポイント

#### どうやって１発でアプリを呼び出すか
いろいろ方法があると思うんですが、

* 汎用的（＝特定のアプリに依存しない）　で、
* リスクが低そう（＝レジストリいじらない）

な手段を検討しました。
軽くググった結果、Windowsの右クリックメニューに用意されている「送る」メニューが比較的カスタマイズしやすい仕様だったのでこちらを利用します。ここからアプリ起動する場合、右クリックメニューの対象となっているファイルパスが第一引数として渡されているようなのでそれを利用します。

#### GoogleDriveAPIのハマりどころ

##### ▽ 新規追加と更新は明確にAPIを呼び分ける必要がある
GoogleDriveではファイル名ではなく独自のIDをもとにファイルを識別しているため、同一ファイルで何度もCreateメソッドを呼んでアップロードしているとドライブ上に同一名称のファイルがいくつも表示されるようになってしまいます。
そこで、同一名称のファイルがアップロード済みであれば更新処理に切り替える仕様にしました。すでにアップロードされているかどうかはFiles.List APIにファイル名を検索条件に渡してファイルの有無で判定してます。

##### ▽ ドメイン共有
アップロードしたファイルに対して、権限変更のリクエストを送る感じに実装すればOK。

##### ▽ 共有リンク
Files.Create APIの戻り値で取得できるWebViewLinkプロパティが使えました。

そんなこんなで、アップロード部分のロジックは↓のようになってます

```DriveSender.cs
private File Upload(string filePath, DriveService service)
{
    var fileName = System.IO.Path.GetFileName(filePath);

    var listRequest = service.Files.List();
    listRequest.Q = $"name = '{fileName}' and '{_Configuration.DriveDirectoryId}' in parents and trashed=false";
    listRequest.Fields = "nextPageToken, files(id, name, webViewLink) ";

    // アップロード済みのファイルを更新するにはAPIを切り替える必要がある

    var meta = new File()
    {
        Name = System.IO.Path.GetFileName(filePath),
    };

    using var stream = new System.IO.FileStream(filePath, System.IO.FileMode.Open);
    
    var file = listRequest.Execute().Files.FirstOrDefault();
    if(file != null)
    {
        // 更新
        var request = service.Files.Update(meta, file.Id, stream, GetMimeType(filePath));

        request.Fields = "id, name, webViewLink";
        request.KeepRevisionForever = false;

        _Logger.Info("Request Files.Update");
        var result = request.Upload();
        if(result.Status == UploadStatus.Failed)
            throw result.Exception;

        request.Body.WebViewLink = file.WebViewLink;

        return request.Body;
    }
    else
    {
        // 新規追加
        meta.MimeType = GetMimeType(filePath);
        meta.Parents = new List<string> { _Configuration.DriveDirectoryId };

        var request = service.Files.Create(meta, stream, GetMimeType(filePath));
        request.Fields = "id, name, webViewLink";
        request.KeepRevisionForever = false;

        _Logger.Info("Request Files.Create");
        var result = request.Upload();
        if (result.Status == UploadStatus.Failed)
            throw result.Exception;

        _Logger.Info("権限変更");
        {
            PermissionsResource.CreateRequest permissionRequest;
            
            if(string.IsNullOrEmpty(_Configuration.Domain))
            {
                permissionRequest = service.Permissions.Create(new Permission
                {
                    Type = "anyone",
                    Role = "reader",
                }, request.ResponseBody.Id);
            }
            else
            {
                permissionRequest = service.Permissions.Create(new Permission
                {
                    Type = "domain",
                    Role = "reader",
                    Domain = _Configuration.Domain,
                }, request.ResponseBody.Id);
            }

            _Logger.Info("Request Permissions.Create");
            permissionRequest.Execute();
        }

        return request.ResponseBody;
    }
}
```

## おわりに
ひとつひとつの操作は大したことないんですけど、一日に何度もやってると地味にストレス溜まってたんですよね。思ってたよりは時間かからなかった（実質２日くらい？）ので、もっと早く作ってしまっとけばよかったなぁとちょっと後悔。テンプレ作業を何度も脳死状態でやりつづけるのはプログラマにとってけしからん状態なのでこれからもバシバシ効率化していきます！


## 参考サイト・記事
https://www.atmarkit.co.jp/ait/articles/1109/30/news131.html

https://developers.google.com/drive/api/v3/quickstart/dotnet

https://qiita.com/hosomin/items/6884fa40c2ba0048c10e

https://qiita.com/iwatuki/items/37dcd2e853b192cbce25