PHP
GraphAPI
OneDrive

phpからOneDriveにgraph apiを使ってファイルをアップロードする

ドキュメント読めばできるだろと思ったそこのあなた!

  1. Microsoftの認証APIは複数あって間違えて使うと思ったことが実現できない。
  2. MicrosoftのAPIは豊富すぎて逆にドキュメントが多すぎてどこを見ればよいかわからない。
  3. 説明は豊富だけど実際にこうやったら良いという例が少ない。(あると思ったらC#だった)
  4. 認証の方法が複数あってよく読まないとはじめての人はよくわかんね、や~めたってなる。

なのでMicrosoftのAPIを使うのはなんだか敷居が高いわけです。今回訳あって自分で「ファイルをOneDriveにアップロード」をPHPのバッチで実装せねばならない状況になったので同じように困ってる人の助けになればと思います。(composerのパッケージは書かないという卑怯者です)

環境

  • PHPでバッチを書こうとしている。
  • office365契約してる。
  • office365管理者アカウントを持っている。

graphのAPI(Oauth)を使えるようにする

  1. Application Registration Potalに行く。
  2. office365の管理者アカウントでログインする。
  3. アプリの追加ボタンを押す。
  4. (急に英語になるけど慌てない)、アプリの名前を入れる。適当で良いが後でみてわかるように。guided setupにはチェックはつけない。
  5. アプリケーションIDというのはAPIではclient_idのこと。あとで使うのでどこかにコピペしておいてください。
  6. シークレットキーを作る、新しいパスワードを生成ボタンを押す。一度だけ表示されるので必ずコピーしてどこかに保存する。
  7. プラットフォームの追加でWEBを選択して、リダイレクトURLに http://localhost を入れる。ログアウトURLは入力しなくて良い。
  8. アプリケーションのアクセス許可のところで、file.readwrite.allを選択する。
  9. バッチで呼べるように管理者の許可を得る。ブラウザで以下のURLを叩いてください。{client_id}は自分のに置き換えてください。

https://login.microsoftonline.com/common/adminconsent?client_id={client_id}&redirect_uri=http://localhost

リダイレクトしたあとにブラウザに怒られる可能性高いです(localhostにWEBサーバが立ってたら別)が無視してもらって、これでAPI呼び出し準備は完了です。ちなみにこの作業はユーザーなしでアクセスを取得に書いてあることと同じことをしています。これをせずにいくと、APIを呼び出すたびにログイン画面が挟まれるのでバッチからは事実上使えないのです。

PHP処理の流れ

  1. アクセストークンを取得します。
  2. graph apiのOneDrive操作のAPIをつかって操作します。

ここまでくればあとはPHPコードを書きまくるまでです。それぞれAPIは微妙に癖ありますけど、そこはそれこそドキュメントを読めばなんとかなります。(ならないときもありますがw)

トークン取得はこんな感じ

// 勝手にguzzleつかってると仮定してるけどw
$options = [
    'form_params' => [
        'client_id' => xxxxxx,
        'scope' => 'https://graph.microsoft.com/.default',
        'client_secret' => xxxxxx,
        'grant_type' => 'client_credentials',
    ],
];
$json = json_decode(
    $guzzle->post(
        'https://login.microsoftonline.com/APIのクライアントID/oauth2/v2.0/token'
        $options
    )
    ->getBody()
    ->getContents()
);

return $json->access_token;

ファイルのアップロード

$options = [
    'headers' => [
        'Authorization' => 'Bearer ' . 'さっき取得したアクセストークン'
    ],
    'body' => fopen('file path', 'r'),
];

$json = json_decode(
    $this->guzzle->put(
        'https://graph.microsoft.com/v1.0/users/{ユーザーID}/drive/items/{親ディレクトリのID}:/{ファイル名}:/content',
        $options
    )
    ->getBody()
    ->getContents()
);

ここだけ見れば大丈夫リンク

余談

packagistにライブラリ落ちてるんじゃ・・・

そう思って色々試してみたんですが、どうもマニアックすぎるのかライブラリの完成度がいまいちでした。微妙にバグってたり使いにくかったりするので結局自分でAPI叩いたほうが速いという結論になりました。

自分のユーザーIDはどうやって取得するか

一番簡単なのは、graph explorerにアクセスして、管理者アカウントでログインして許可して/meにアクセスしたら返ってくるjsonにID書いてます。

ディレクトリIDはどうやって取得するか

rootディレクトリはいいんですが、そのサブディレクトリのサブディレクトリのIDはそう簡単には取得できない。(と思ってますが、知ってる方いればぜひ)。私がやってたのはAPIを叩いたら返ってくるjsonの中に書いているのでそれを使っていました。ファイルをアップロードした時とかフォルダを作ったときとかの結果jsonの中にidが書いてあるのでそれを使うイメージです。

アクセス権を変更したときは再度管理者の許可をブラウザで得る必要がある

例えばcalendar.readみたいなのをあとから追加したときは再度ブラウザでURL叩いて管理者の同意が必要(/adminconsentってやつです)です。これわからなくて小一時間悩みました。

まとめ

たぶんまともにそのままAPI叩いていく人は少ないと思うけど、もしPHPじゃなくてもバッチでOneDriveにアップロードしないといけないという状況なら多少のお役に立てるのではないでしょうか。