Google Apps ScriptとGraph APIでOneDriveアップローダを作る【GAS】

会社としてのファイルストレージはBoxを利用しつつも、外部共有用にということでOneDriveを使うといったような使い分けをしてる事例は多いかと思います。Boxではなるべく外部共有をせず、またOneDriveには会社の情報を置かず、外部の企業とやり取りする専用として使うといったような事例になります。

この時、いちいちOneDriveを開いてアップして共有リンクを取得して相手にメールを送るというのは非常に億劫なので、OneDriveとGoogle Apps Scriptを利用して、アップローダを作れないか?ということで実験してみました。

今回使用するスプレッドシート等

GASはスクリプト実行が最大6分の制限がある為、GASでアップロードとなるとファイルサイズが限られる上に回線状況によってアップできないといったことが出る可能性があります。故にHTML側で稼働するGraph APIでアップロードをさせて共有リンクを得るのが今回の目的。

GAS側は認証とAccess Tokenの処理を中心に実装します。過去にBox file Pickerで実装したことがあります。

OneDrive Pickerという仕組みもあるのですが、GASでは動作しませんでした。

Google Apps ScriptでBox APIを叩いて権限変更をする【GAS】

Google Apps Scriptで6分の壁(タイムアウト)を突破する【GAS】

事前準備

利用するためには事前にAzure ADでアプリプロジェクトを作成し、認証を経てAccess Tokenを取得する必要性があります。実際にアップロードを行うのはHTML側となるため、役割分担をはっきりさせています。

GAS側の事前準備

ライブラリの追加

以下の手順でOAuth2 for Apps Scriptライブラリを追加しましょう。

  1. スクリプトエディタを開きます。
  2. メニューより「リソース」⇒「ライブラリ」を開きます。
  3. ライブラリを追加欄に「1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF」を追加します。
  4. 今回はバージョンは43を選択してみます。
  5. 保存ボタンを押して完了

これで、OAuth2.0認証にまつわる様々な関数を手軽に利用できるようになります。

図:ライブラリを追加した様子

コールバックURLを取得する

コールバックURLとは、認証を完了しAccess Tokenを取得したら戻るべきURLを指定するものです。これは、スクリプトIDをもとに作られているので、スクリプトIDを取得して組み立てます。

  1. スクリプトエディタのメニューより「ファイル」⇒「プロジェクトのプロパティ」を開く
  2. 情報の中にある「スクリプトID」を控えておく。
  3. https://script.google.com/macros/d/スクリプトID/usercallback として組み立てる。これがコールバックURLとなる。

図:スクリプトIDはファイル毎に異なるのです。

Azureでプロジェクトを作成

  1. アプリの登録にて登録を開始する
  2. 新規登録をクリックする
  3. 名前を入力(今回はonedriveuploadと入力しました)、リダイレクトURIは「webを選択しhttps://script.google.com/macros/d/スクリプトID/usercallback」を入力。
  4. 登録ボタンをクリックする
  5. 出てきた中で、「アプリケーション(クラと書かれているのがクライアントID」なので、このコードをメモしておく
  6. 左サイドバーより、「証明書とシークレット」をクリック
  7. 新しいクライアントシークレット」をクリックする
  8. 今回は12か月期限で追加をクリック
  9. これで値に「クライアントシークレット」が生成されて手に入りました(値の項目がそれになる)。このシークレットはこの時だけしか表示されないので、注意してください。
  10. つづけて、左サイドバーより「APIのアクセス許可」をクリックする
  11. Microsoft APIの中にある「Microsoft Graph」をクリックする。
  12. 委任されたアクセス許可」をクリックする
  13. アクセス許可の追加をクリックする
  14. 管理者の承認が必要なものの場合、追加出来たら、xxxxxに管理者の同意を与えますをクリックします。すると、状態が緑色になります。
  15. デフォルトでUser.ReadがすでにONなので、今回はopenid、offline_access、Files.ReadWrite.All、Sites.Read.Allを検索してONにしましょう。(Files.ReadWrite.Allにしないとアップロードが出来ません)
  16. 次に左サイドバーより「認証」をクリック
  17. 暗黙の付与にて、「アクセストークン」にチェックを入れる
  18. サポートされているアカウントの種類に於いては、「マルチテナント」にしておきました。
  19. 保存をクリック
  20. 概要のエンドポイントをクリックすると、いろいろなエンドポイントURLが出る。
  21. 概要のディレクトリ(テナントの数値はメモっておきます。あとでプログラム中で使用します。

図:追加したスコープ一覧

図:Access Tokenを取得して使えるようにする

OneDriveにフォルダを作っておく

今回はOneDriveのルート直下に「テストマン」というフォルダを作っておき、そこにアップロードするという仕様にしています。ユーザが自由にフォルダを作ってアップロードさせるという場合には、OneDriveにフォルダを作るAPIを事前に叩いてから、そのフォルダ名でもってアップロードするという仕組みが別途必要です。

注意点

GASでファイルのアップロードを行わせるとなると、色々と制限事項に引っかかってしまいます。主な制限事項は

  1. GASの実行最大時間は6分まで(6分以内にアップロードが終わらないサイズの場合、アップロードが出来ません)
  2. POSTで送信できるファイルサイズが上限50MBまでとなってる
  3. OneDrive Businessは1TBが容量。E3以上が5TBまで使えますが、アップするファイルサイズは最大250GBまで。

そうなると、50MB以上の巨大なファイルを送りたい場合、途中でセッション切れやファイルサイズ制限で送れないことになります。そこで今回はGraph APIを使うに当たって認証とAccess Tokenの処理はGAS側で行わせ、アップロード作業はHTML側でFetch APIを使ってリクエストを送るという方法で回避しています。

認証を行う処理を作成する

OAuth2 for Apps Scriptのページの「Create the OAuth2 Service」にあるコードを元に、Google Apps Script側で構築をします。この時、Microsoft365側で取得したアプリケーションIDやシークレットを使います。また、今回はいつもよりも要求するアクセス権限が多い点に注意。

認証実行はstartoauthを叩くのですが、事前にウェブアプリケーションとしてデプロイしておく必要があります。

GAS側コード

  • 今回利用するGraph APIのエンドポイントはhttps://graph.microsoft.com/betaとなります。
  • 要求する権限はscopeに半角スペースで区切って、Azure AD側で用意したものと同じものを設定します。
  • startoauthを実行して認証を実行すれば、スクリプトプロパティにAccess Tokenが格納されます。

HTML側コード

  • 実際にこれらのコードで、startoauthを実行すると、スプレッドシート上で認証用のダイアログが出ます。
  • 認証でMicrosoft365アカウントにログインします
  • 取得したAccess Tokenほかはスクリプトプロパティのoauth2.Graphという項目にガッツリ値が格納されます。ここにはAccess Token, Refresh Token, expire_inのタイムなどが入っています。
  • reset関数はログアウトされて、再度認証ができるようになります。

ソースコード

GAS側コード共通

  • GAS側は共通です。Web表示やシートデータの取得と返却、Access Tokenの取得などが仕事になります。
  • また、アップロード後の情報を記録するのもGAS側の仕事になります。

小さいファイルのアップロード

JS側コード

  • 小さいファイルをアップロードする機能はわりと簡単に実装出来ます。最大4MBまでとなります。
  • アップロードが完了すると、共有リンクのURLやファイル情報を取得できます。
  • 起動時にinputの中身を空にして、完了後にダイアログを閉じます。
  • 今回は同じファイル名があった場合には自動リネームするようにendpointのURLに指定しています。

図:簡単なアップロードダイアログ

図:アップロード出来た

HTML側コード

  • ファイルアップ用のinputとボタンを用意してるだけ。
  • ボタンはuploadOneDrive2を叩くだけのシンプル仕様

大きいファイルのアップロード

今回のコードはピュアなJavaScriptとHTMLのみで構築しています。但し、こちらの方式は4MBの制限なく大容量のファイルをアップロードできるのですが、かなり複雑なコードになります。

JS側コード

  • こちらのサイトのコードをベースに必要な情報を得る為のコードを追加しています。
  • GASの制限を回避する為にHTML側のJSでGraph APIを叩いています。
  • リジュームアップロードが必要で、最大320KB毎に分割してアップロードする必要があります。
  • 前項の方式と違い、payloadとして重複時には自動リネームを明示的に指定する必要があります。
  • createUploadSessionでまずは、アップロード先URLというものを取得します(このときのメソッドはPOSTである必要がある)
  • アップロード先URLを手に入れたら、fetchmanという関数に渡して同期的にアップロードする必要があります。
  • 一番最後のアップロードだけファイルの分割サイズを残りの端数で指定するように調整が必要です。
  • アップロード先URLに対してのリクエストはPUTである必要があります。
  • inputにアップしたblobデータをsliceで指定サイズで切り出してbodyに指定します
  • content-lengthやcontent-rangeというヘッダでの指定方法がかなり独特なので注意。
  • response.json()で変換したデータに、ダウンロードURL等が入っています。responseそのものの中にはステータスなどが入っています。
  • 途中まではステータスは202なのですが、最後のアップロードだけは201, 200で返ってくるので、その際に共有リンクを取得するようにします。

図:無事にアップと自動リネーム出来た

HTML側

  • ファイルアップ用のinputとボタンを用意してるだけ。
  • ボタンはuploadOneDriveを叩くだけのシンプル仕様

図:こんな感じのアップ画面

共有リンクのURLを生成する

前述までに紹介した「downloadUrl」や「webUrl」なのですが、クリックすると分かりますが「直接ダウンロード」のURLとなっています。直リンクであるため、いきなりプレビュー無でダウンロードされる。一方でウェブ上で共有をクリックした時のURLは別途生成してあげる必要があります。こちらのリンクの場合は、OneDrive上のプレビューアで開かれることになるので、ExcelやCSVはExcel Onlineにて、動画類はstreamで開かれるようになります。

以下のコードはGAS側で生成して返すようにしています。

  • この共有リンクを生成するとeTagの値がカウントアップされてしまうので注意。
  • createLinkに対してPOSTでリクエストを投げます。
  • payloadの中身で共有範囲や権限、パスワードの設定等が可能です。
  • この時生成されるwebUrlは前述のアップ時のレスポンスには含まれていないので、このリクエストで生成する必要があります。

図:共有リンク生成画面のここから生成するものを取得する

eTagの値を取得する

Microsoft Graph APIでのデータの更新や削除を行うには、ファイルに割り当てられているeTagという値をリクエストに追加しないと、実行が出来ません。このeTagの値は共有リンクの生成やファイルの上書きアップロードなど手動で変更を加えると変わるという性質があります。よって、削除や更新時には最新のeTagの値を取得してから更新を掛ける必要があります。

以下のコードはGAS側で生成して返すようにしています。

  • 取得されるeTagの値は「ダブルコーテーション」が前後についた状態で取得する必要があるので、除外してはいけません。
  • GETで取得して、レスポンス内にeTagというのがあるのでこれを拾って返します。

実行した結果、以下のような値が取得されます。前の数値部分は更新や共有リンク生成しても変わらないのですが、カンマより後ろの数値が更新のたびに1つずつカウントアップしていく仕様になっています。

ファイルを削除する

アップロードしたファイルのIDをスプレッドシートに記録しておき、後で削除や一定日数経過したファイルを自動削除のトリガーで処理してしまうといった場合、Graph APIでOneDrive上のファイルを削除する関数を用意する必要があります。この時、前述の通りeTagの値が必要になるので、リクエストがやや複雑です。

  • 関数はasyncで実行する必要があります。
  • 前述のnowetagvalue関数を使って現在のeTagの値を取得させます。
  • リクエストヘッダにif-MatchでeTagの値を含めます
  • リクエストはDELETEメソッドで行います。
  • レスポンスコードが204であれば削除成功です。

リクエスト時のeTagの値がおかしかったり付け忘れると、レスポンスコードは「409 Conflict」「412 Precondition Failed」で返ってきてしまいますので、常に最新の情報を取得するようにします。このあたりは過去にPlannerの操作でも遭遇して苦しめられました。

VBAとMicrosoft Graph APIの連携 – Planner編

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)