Closed75

Box API の Java SDK を試してみる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

このスクラップについて

今後の仕事で Box API の Java SDK を使うかも知れないので今のうちに基本的な使い方を調べておこうと思う。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Box サインアップ

Box のアカウントを持っていないので作成してみる。

https://www.box.com/ja-jp/pricing/individual

個人向けの Individual プランであれば無料で使用できる。

サインアップはメールアドレスか Google アカウントでできる。

今回は手軽に Google アカウントを使用する。

サインアップするとすぐに利用できる、素晴らしい。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

アップロード

動作確認のためにあると便利そうなので事前に適当なファイルを作成してアップロードしておく。

コマンド
touch hello-box.txt
hello-box.txt
HELLO BOX!

Web UI にドラッグ&ドロップするとファイルをアップロードできる。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

開発者コンソール

Box DEV の開発者コンソールに移動ボタンを押すと開発者コンソールページへ移動する。

ページ上部の「開発者コンソールのすべての機能にアクセスするには、Box Developerアカウントにログインまたはサインアップしてください。」というメッセージが気になるが一旦放置しておく。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

アプリ作成

開発者コンソールのアプリの新規作成ボタンを押すとアプリの新規作成ページが表示される。

アプリの種類は下記の 3 つ。

  • カスタムアプリ
  • アクセス制限付きアプリ
  • Box Custom Skill

それぞれの詳しい説明は下記ページが参考になりそう。

https://ja.developer.box.com/guides/applications/custom-apps/

https://ja.developer.box.com/guides/applications/limited-access-apps/

https://ja.developer.box.com/guides/applications/custom-skills/

認証については下記ページが参考になりそう。

https://ja.developer.box.com/guides/authentication/select/

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

カスタムアプリの認証方法

  • サーバー認証(JWT使用)
  • ユーザー認証(OAuth 2.0)
  • サーバー認証(クライアント認証情報許可)

OAuth 2.0 がスタンダードな方法だと思っていたけど 1 番目に JWT があることを見るとこちらの方がスタンダードなのだろうか?

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

認証方法の選択

https://ja.developer.box.com/guides/authentication/select/

ページの最後の方にある比較表がとてもわかりやすい。

質問 OAuth 2.0 JWT クライアント資格情報 アプリトークン
ユーザーの関与が必要? はい いいえ いいえ いいえ
管理者の承認が必要? いいえ はい はい はい
他のユーザーの代理で操作可能? はい はい はい いいえ
ユーザーにBoxを表示? はい いいえ いいえ いいえ
App Userを作成可能? いいえ はい はい いいえ

カスタムアプリで使用する場合は下記 3 つの認証方法を利用できる。

  • OAuth 2.0
  • JWT
  • クライアント資格情報

一般ユーザー向けのアプリの場合は OAuth 2.0 だが何らかのシステムで Box 連携機能を作る場合には JWT かクライアント資格情報を使うことになりそう。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Box ドキュメントは日本語が秀逸

今のところ全てのページを日本語で読めてしかも日本語訳も違和感がない、めちゃ助かる。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日はここまで

時間が来てしまった。

次回はサーバー認証の設定を行いたい。

何となくだがクライアント資格情報が一番簡単そうなのでクライアント資格情報から試してみようと思う。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

カスタムアプリ作成

サーバー認証(クライアント資格情報許可)でカスタムアプリを作成する。

アプリ名は My First App とした。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

前提条件

ドキュメントによると下記 3 つが必要になる。

  • カスタムアプリ(作成済み)
  • クライアントシークレットの取得
  • Box 管理コンソールからアプリケーションを承認
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

クライアントシークレットの取得

カスタムアプリのページの構成タブを選ぶ。

OAuth 2.0 資格情報のセクションにあるクライアントシークレットを取得ボタンを押す。

下記のアラートメッセージが表示される。

この操作を実行するには、2要素認証を有効にする必要があります。[Settings ボタン]

2 要素認証を有効にする必要がありそうなので [Settings ボタン] を押す。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

2 要素認証の設定

2 段階認証セクションにある設定ボタンを押す。

認証方法には認証アプリ、SMS、メールを使える、今回は認証アプリを使用する。

QR コードが表示されたらスマホの認証システムアプリなどで読み取って 6 桁のコードを入力する。

デバイスを紛失した時のためにアカウントのバックアップコードが表示されるので控えておく。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

クライアントシークレット取得の再挑戦

開発者コンソールに戻ってクライアントシークレットの取得ボタンを押すと 2 段階認証ページが表示される。

正しい認証コードを入力すると元のページに戻るのでクライアントシークレットの取得ボタンを再度押すとクライアントシークレットが表示される。

コピーボタンを押してクリップボードにコピーする。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

残りは承認

これでクライアントシークレットを取得できたので 3 つの前提条件のうち 2 つが満たされた。

残りは承認だけ、関連ドキュメントは下記の通り。

https://ja.developer.box.com/guides/authorization/

承認と翻訳されると少し戸惑うが英語では authorization なので認可と思えばなんとなく理解できる気がする。

今はまだ十分に理解できていないが、承認のしくみを使って作成したカスタムアプリにユーザー(例えば僕)の Box ファイルにアクセスする権限などを与えるということなのかな?

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

もしかして個人アカウントの場合は承認する必要がない?

個人アカウントの場合は特に承認とかしなくてもデフォルトでカスタムアプリが自分のアカウントの情報を読み書きできる?

とりあえずやるだけやってみよう。

できなかったとしてもエラーメッセージとかで何かがわかるかもしれない。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

まずは Node.js で試す

あまり使っていない Java から始めるとハマりそうなので勝手知ったる Node.js から始めてみる。

まずはワークスペースを作成する。

コマンド
mkdir hello-box-api
cd hello-box-api
npm init -y
npm install --save dotenv box-node-sdk
npm install --save-dev ts-node @types/node
touch getting-started.ts .env
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

コーディング

getting-started.ts
import BoxSDKNode from "box-node-sdk";

async function main() {
  const sdk = new BoxSDKNode({
    clientID: process.env.BOX_CLIENT_ID!,
    clientSecret: process.env.BOX_CLIENT_SECRET!,
  });

  const client = sdk.getBasicClient(process.env.BOX_DEVELOPER_TOKEN!);
  const user = await client.users.get(client.CURRENT_USER_ID);

  console.log(JSON.stringify(user, null, 2));
}

main().catch((err) => console.error(err));
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

環境変数

.env
BOX_CLIENT_ID="xxxx"
BOX_CLIENT_SECRET="yyyy"
BOX_DEVELOPER_TOKEN="zzzz"

client ID と client secret は既に取得できたが開発者トークンは初めてだ。

開発者トークンを生成ボタンを押すと表示される。

開発トークンとは何だろうという感じだけどしっかり説明が記載されている。

開発者トークンを使用して、ユーザーとしてアクセスをテストするかスクリプトを作成します。開発者トークンを使用すると、Box APIを使用して個人用のBoxアカウントにのみアクセスできます。このトークンは60分間有効です。Enterprise全体にアクセスするには、Enterpriseへのアクセス権限をリクエストし、承認を受けるためにアプリを送信します。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

OAuth 2.0 や JWT の場合

クライアント資格情報許可の場合は client ID と client secret が発行されるけど他の場合はどうなんだろう。

気になるので作成してみた所、OAuth 2.0 や JWT のいずれの場合も client ID と client secret が発行されていた。

どうやら client ID とclient secret はクライアント資格情報許可に固有のものではないようだ。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

動作確認

コマンド
npx ts-node -r dotenv/config getting-started.ts
実行結果(例)
{
  "type": "user",
  "id": "12345678901",
  "name": "薄田達哉",
  "login": "susukida@example.com",
  "created_at": "2023-04-13T22:14:29-07:00",
  "modified_at": "2023-04-16T17:20:45-07:00",
  "language": "ja",
  "timezone": "Asia/Tokyo",
  "space_amount": 10737418240,
  "space_used": 10,
  "max_upload_size": 262144000,
  "status": "active",
  "job_title": "",
  "phone": "",
  "address": "",
  "avatar_url": "https://app.box.com/api/avatar/large/12345678901",
  "notification_email": []
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

せっかくなのでファイルを表示してみる

https://ja.developer.box.com/reference/get-folders-id-items/

コマンド
touch get-items.ts
get-items.ts
import BoxSDKNode from "box-node-sdk";

async function main() {
  const sdk = new BoxSDKNode({
    clientID: process.env.BOX_CLIENT_ID!,
    clientSecret: process.env.BOX_CLIENT_SECRET!,
  });

  const client = sdk.getBasicClient(process.env.BOX_DEVELOPER_TOKEN!);
  const folderID = "0"; // ルートフォルダは常に ID 0 で表されます。
  const getItemsResult = await client.folders.getItems(folderID);

  console.log(JSON.stringify(getItemsResult, null, 2));
}

main().catch((err) => console.error(err));
コマンド
npx ts-node dotenv/config get-items.ts
実行結果(例)
{
  "total_count": 1,
  "entries": [
    {
      "type": "file",
      "id": "1190396933725",
      "file_version": {
        "type": "file_version",
        "id": "1297794363325",
        "sha1": "c47b8e953b00f2ff97e28ce6b7c9b693d8f20fe4"
      },
      "sequence_id": "0",
      "etag": "0",
      "sha1": "c47b8e953b00f2ff97e28ce6b7c9b693d8f20fe4",
      "name": "hello-box.txt"
    }
  ],
  "offset": 0,
  "limit": 100,
  "order": [
    {
      "by": "type",
      "direction": "ASC"
    },
    {
      "by": "name",
      "direction": "ASC"
    }
  ]
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

せっかくなのでファイル内容を表示してみる

https://ja.developer.box.com/reference/get-files-id-content/

コマンド
touch get-content.ts
get-items.ts
import BoxSDKNode from "box-node-sdk";

async function main() {
  const sdk = new BoxSDKNode({
    clientID: process.env.BOX_CLIENT_ID!,
    clientSecret: process.env.BOX_CLIENT_SECRET!,
  });

  const client = sdk.getBasicClient(process.env.BOX_DEVELOPER_TOKEN!);
  const folderID = "0"; // ルートフォルダは常に ID 0 で表されます。
  const { entries } = await client.folders.getItems(folderID);

  for (const entry of entries) {
    const readStream = await client.files.getReadStream(entry.id);
    const buffer = await new Promise<Buffer>((resolve, reject) => {
      const chunks: Buffer[] = [];

      readStream.on("data", (chunk: Buffer) => chunks.push(chunk));
      readStream.on("end", () => resolve(Buffer.concat(chunks)));
      readStream.on("error", (err: Error) => reject(err));
    });

    console.log(buffer.toString());
  }
}

main().catch((err) => console.error(err));
コマンド
npx ts-node dotenv/config get-content.ts
実行結果(例)
HELLO BOX!

一発で動いたみたいな感じになっているけど実際にはちょっと試行錯誤した。

まずカスタムアプリのアプリケーションスコープセクションで Box に格納されているすべてのファイルとフォルダへの書き込みにチェックを入れる。

書き込まないのに何故だろうという感じだが仕方がない。

チェックを入れたら変更を保存ボタンを押す。

変更を反映するために開発者トークンを取り消して再び開発者トークンを生成ボタンを押す。

新しい開発者トークンで .env を書き換える。

以上で動くようになった。

やはりファイル内容が表示されると嬉しい。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日こそ Box API Java SDK を使う

だいぶ前置きが長くなってしまったが今日こそ本題の Box API Java SDK を使うことを目標にする。
具体的には下記の 3 点について検証したい。

  • 認証(ユーザー認証|OAuth 2.0)
  • フォルダ作成
  • 分割アップロード
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

IntelliJ IDEA プロジェクト作成

IntelliJ IDEA を起動する

New Project ボタンを押す。

Build system については悩ましいが Gradle にした、IntelliJ ってなんだ?

Create ボタンを押す。

無事にプロジェクトが作成された。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

とりあえずコミット

IntelliJ IDEA では Command + K で Git コミットできる。

Commit ボタンを押すか Command + Enter でコミットされる。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

環境変数の設定

メニュー > Run > Edit Configurations... を選ぶと Run / Debug 構成を編集できる。

環境変数(Environment variables)の入力部にセミコロン区切りで環境変数を KEY=value 形式で入力する。

環境変数の読み込み
String accessToken = System.getenv("BOX_ACCESS_TOKEN");
System.out.println(accessToken);
実行結果
xxxx

▶︎ ボタンを押す代わりに Ctrl + R でも main() 関数を実行できる。

環境変数の設定は .idea/workspace.xml に保存され、このファイルは .idea/.gitignore でバージョン管理対象から外されているので安心して Git にコミットできる。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

コーディング

src/main/java/org/example/Main.java
package org.example;

import com.box.sdk.BoxAPIConnection;
import com.box.sdk.BoxFolder;
import com.box.sdk.BoxItem;

public class Main {

    public static void main(String[] args) {
        String accessToken = System.getenv("BOX_ACCESS_TOKEN");
        BoxAPIConnection api = new BoxAPIConnection(accessToken);
        BoxFolder rootFolder = BoxFolder.getRootFolder(api);

        for (BoxItem.Info itemInfo : rootFolder) {
            System.out.format("[%s] %s\n", itemInfo.getID(), itemInfo.getName());
        }
    }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

フォルダー作成

フォルダー作成には親フォルダーの createFolder() メソッドを呼び出す。

https://github.com/box/box-java-sdk/blob/v4.0.1/doc/folders.md#create-a-folder

src/main/java/org/example/Main.java
package org.example;

import com.box.sdk.BoxAPIConnection;
import com.box.sdk.BoxFolder;
import com.box.sdk.BoxItem;

public class Main {
    public static void main(String[] args) {
        // mainGettingStarted();
        mainCreateFolder();
    }

    public static void mainCreateFolder() {
        String accessToken = System.getenv("BOX_ACCESS_TOKEN");
        BoxAPIConnection api = new BoxAPIConnection(accessToken);
        BoxFolder rootFolder = BoxFolder.getRootFolder(api);

        String createdFolderName = "New folder created by Java SDK";
        rootFolder.createFolder(createdFolderName);
    }
}

前のコードを残しておきたいので mainCreateFolder() メソッドを作成した。

開発者トークンが時間切れになっていたので再発行して環境変数を設定し直した。

Ctrl + R で実行したところ正常に終了した。

Box Web UI を開いた所、フォルダーが作成されていることを確認できた。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

フォルダーの重複作成

同じ名前のフォルダーを作成しようとしたらどうなるだろう?

実際にやってみたところ例外が発生した。

例外の内容を表示するために main() メソッドに変更を加える。

src/main/java/org/example/Main.java(抜粋)
    public static void main(String[] args) {
        try {
            mainCreateFolder();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }
エラーメッセージ
The API returned an error code [409 | tps6gihd3svaellc.16a397f56c28e189a422b2e323039d6af] item_name_in_use - Item with the same name already exists

エラーメッセージがとてもわかりやすい。

ちなみにステータスコード 409 は Conflict(競合)。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

同じ名前のファイルがあった場合はどうなる?

興味深かったのでやってみた。

src/main/java/org/example/Main.java(抜粋)
        String createdFolderName = "hello-box.txt";
        rootFolder.createFolder(createdFolderName);
エラーメッセージ
The API returned an error code [409 | 4ayapohd3t1208xf.0f052da01dd64183ec7a60835d5fa2953] item_name_in_use - Item with the same name already exists

結果は同じ名前のフォルダーがあった場合と同じだった。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

フォルダー作成の改良版

src/main/java/org/example/Main.java
package org.example;

import com.box.sdk.BoxAPIConnection;
import com.box.sdk.BoxFolder;
import com.box.sdk.BoxItem;

public class Main {
    public static void main(String[] args) {
        try {
//            mainGettingStarted();
//            mainCreateFolder();
            mainCreateFolderIfNotExists();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    public static void mainCreateFolderIfNotExists() {
        String accessToken = System.getenv("BOX_ACCESS_TOKEN");
        BoxAPIConnection api = new BoxAPIConnection(accessToken);
        BoxFolder rootFolder = BoxFolder.getRootFolder(api);
        String createdFolderName = "New folder created by Java SDK";
        boolean found = false;

        for (BoxItem.Info itemInfo : rootFolder) {
            if (itemInfo.getName().equals(createdFolderName)) {
                found = true;
                break;
            }
        }

        if (!found) {
            rootFolder.createFolder(createdFolderName);
            System.out.println("Created");
        } else {
            System.out.println("Not created");
        }
    }
}
実行結果
Not created
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

ファイルアップロード

API リファレンスに書かれている通りファイルのアップロードには分割の有無で 2 パターンある。

https://ja.developer.box.com/reference/post-files-content/

https://ja.developer.box.com/reference/post-files-upload-sessions/

分割アップロードをするには uploadLargeFile() メソッドを使えば良いのかな?

https://github.com/box/box-java-sdk/blob/v4.0.1/doc/files.md#upload-a-large-file-in-chunks

Main クラスのソースコードをアップロードしてみる。

src/main/java/org/example/Main.java
package org.example;

import com.box.sdk.BoxAPIConnection;
import com.box.sdk.BoxFolder;
import com.box.sdk.BoxItem;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
//            mainGettingStarted();
//            mainCreateFolder();
//            mainCreateFolderIfNotExists();
            mainUploadFile();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    public static void mainUploadFile() throws IOException, InterruptedException {
        String accessToken = System.getenv("BOX_ACCESS_TOKEN");
        BoxAPIConnection api = new BoxAPIConnection(accessToken);
        BoxFolder rootFolder = BoxFolder.getRootFolder(api);

        String uploadedFilePath = "src/main/java/org/example/Main.java";
        File uploadedFile = new File(uploadedFilePath);
        FileInputStream inputStream = new FileInputStream(uploadedFile);
        String createdFileName = "New file created by Java SDK";
        long fileSize = uploadedFile.length();
        rootFolder.uploadLargeFile(inputStream, createdFileName, fileSize);

        System.out.println("Succeed to upload");
    }
}

実行した所なんと下記のエラーが発生した。

エラーメッセージ
The API returned an error code [400 | a28964f1bbf9aa98d7e2991334c71517] file_size_too_small - File size 2737 less than minimum allowed for this API: 20000000

どうやら 2MB 以上でないとこの uploadLargeFile() メソッドは利用できないようだ。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

ファイルアップロードの改良版

下記のような条件分岐を追加した。

src/main/java/org/example/Main.java(抜粋)
        if (fileSize >= 2000000) {
            rootFolder.uploadLargeFile(inputStream, createdFileName, fileSize);
        } else {
            rootFolder.uploadFile(inputStream, createdFileName);
        }
実行結果
Succeed to upload

Box Web UI を確認するとファイルが作成されていることがわかる。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日わかったこと

  • フォルダ作成は同じ名前のフォルダやファイルがあると失敗する。
  • アップロードについては 2MB 以下だと分割アップロードができないことがわかり、ファイルサイズに応じて分割/非分割アップロードを使い分ける必要がある。
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日はここから

今日はユーザー認証(OAuth2.0)について調べる。

余力があればサーバー認証(JWT)やカスタムアプリ以外のアプリについても調べたい。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

認証についての説明が GitHub の方が詳細

しかもわかりやすい。

https://github.com/box/box-java-sdk/blob/v4.0.1/doc/authentication.md

認証方法には下記の 5 つがある。

  • 開発者トークン
  • サーバー認証(JWT)
  • OAuth 2.0
  • アプリトークン
  • クライアント資格情報

開発者トークンは client ID や client secret なしで単体でアクセスできるようだ。

また Client Credentials Grant(クライアント資格情報)に興味深い記述がある。

Obtaining User token

To obtain user account you will have to provide user ID with client id and secret

BoxCCGAPIConnection api = BoxCCGAPIConnection.userConnection(
    "client_id",
    "client_secret",
    "user_id"
);

In order to enable generating user token you have to go to your application configuration that can be found here. In Configuration tab, in section Advanced Features select Generate user access tokens. Do not forget to re-authorize application if it was already authorized.

ユーザートークン(ユーザーアクセストークン)を使うことで Box 個人アカウントの場合でもアクセスできるようになるのかな?

個人アカウントの場合はユーザー ID ではなくアカウント ID なので何となく違う気もする。

それともサーバー認証を使用するカスタムアプリは Enterprise 版でしか使えないのかな?

https://support.box.com/hc/ja/articles/360044195293

何となくだがそんな感じがする。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

OAuth 2.0 を試す

サーバー認証(JWT / クライアント資格情報)を使う認証についてはエンタープライズ版じゃないと検証ができない可能性が高いことがわかったので、まずは OAuth 2.0 の方を試してみようと思う。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

承認 URL へのリダイレクト

Web サーバーを使用している場合はサーバーサイドまたはクライアントサイドで承認 URL へリダイレクトする。

サーバーサイドでリダイレクト(例)
response.redirect(authorizationUrl)
クライアントサイドでリダイレクト(例)
window.location.assign(authorizationUrl)

Box API Java SDK には承認 URL を生成するメソッドが無いので手動で作る必要がある。

承認 URL の作成
String authorizationUrl = "https://account.box.com/api/oauth2/authorize?client_id=[CLIENT_ID]&response_type=code";

プログラムを書いても良いが動作を確認するだけなら手動で [CLIENT_ID] の部分を書き換えるだけで大丈夫そう。

[CLIENT_ID] の部分を書き換えた URL にアクセスする。

例えば xxxx に書き換えた場合は https://account.box.com/api/oauth2/authorize?client_id=xxxx&response_type=code などにアクセスする。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

承認コードの取得

Box へのアクセスを許可ボタンを押すと http://localhost:3000 にリダイレクトされる。

http://localhost:3000 では Web サーバーを立ち上げていないので当然ながらエラーページが表示される。

ブラウザのアドレスバーに ?code=xxxx のように承認コードが付加されているのでこれを控えておく。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

承認コードの有効期間は数秒

このコードはアクセストークンではなく、有効期間はほんの数秒です。SDKを使用すると、このコードを実際のアクセストークンと交換できます。

手動でコピー&ペーストしてアクセストークンと交換しようと思っていたので残念だが、有効期間があまり長くてもリスクが高そうなので仕方ない。

Java で Web サーバーを作る勉強をする必要がありそうだ。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

戻ってきた

Spring Boot クイックスタートがとても素晴らしかったので 1 時間もかからずに終わってしまった。

別のスクラップを作る必要がなかったかも知れない。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Spring Boot プロジェクトに Box API Java SDK 追加

build.gradle に依存関係を追加する。

build.gradle
dependencies {
	implementation 'com.box:box-java-sdk:4.0.1'
}

Gradle ツールウィンドウの左上の更新 🔃 ボタンを押して変更を反映する。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

コーディング

src/main/java/com/example/demo/DemoApplication.java
package com.example.demo;

import com.box.sdk.BoxAPIConnection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/oauth2")
    public String OAuth2redirect() {
        String clientID = System.getenv("BOX_CLIENT_ID");
        String authorizationUrl = "https://account.box.com/api/oauth2/authorize?client_id=" + clientID + "&response_type=code";

        return "<a href=\"" + authorizationUrl + "\">" + authorizationUrl + "</a>";
    }

    @GetMapping("/oauth2callback")
    public String OAuth2Callback(@RequestParam(value = "code", defaultValue = "") String authorizationCode) {
        String clientId = System.getenv("BOX_CLIENT_ID");
        String clientSecret = System.getenv("BOX_CLIENT_SECRET");
        BoxAPIConnection client = new BoxAPIConnection(clientId, clientSecret, authorizationCode);

        return String.format("Access token: %s!", client.getAccessToken());
    }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

動作確認

http://localhost:8080/oauth2 にアクセスすると下記のようなリンクが表示される。

oauth2
https://account.box.com/api/oauth2/authorize?client_id=xxxx&response_type=code

リンクをクリックすると承認ページが表示されるので承認すると http://localhost:8080/oauth2/oauth2callback にリダイレクトされる。

リダイレクト先では下記のようにアクセストークンが表示される。

oauth2callback
Access token: xxxx
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

アクセストークン動作確認

せっかくなのでルートフォルダーを一覧してみる。

src/main/java/com/example/demo/DemoApplication.java(抜粋)
    @GetMapping("/list")
    public String list(@RequestParam(value = "accessToken", defaultValue = "") String accessToken) {
        BoxAPIConnection client = new BoxAPIConnection(accessToken);
        BoxFolder rootFolder = BoxFolder.getRootFolder(client);
        StringBuilder response = new StringBuilder();

        response.append("<ul>");

        for (BoxItem.Info itemInfo : rootFolder) {
            response.append(String.format("<li>%s</li>", itemInfo.getName()));
        }

        response.append("</ul>");

        return response.toString();
    }

http://localhost:8080/list?accessToken=xxxx にアクセスする。

xxxx の部分をアクセストークンで置き換える。

成功すると下記のようなリストが表示される。

  • New folder created by Java SDK
  • hello-box.txt
  • New file created by Java SDK
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日わかったこと

  • サーバー認証(JWT or クライアント資格情報)はエンタープライズ版じゃないと検証できなさそう
  • ユーザー認証(OAuth 2.0)のやり方
  • 承認コードの有効期間は数秒程度と短いので動作確認には Web サーバーを作る必要がある
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

おわりに

正直に言うとスクラップを書き始める時は Box 自体知らなかったけどドキュメントや API や SDK がわかりやすかったのはとても良かった。

エンタープライズ向け Dropbox みたいな位置付けなのかな?

クラウドストレージサービスは様々な場面で使えそうなので今回 API を使う方法について学べて良かった。

Box Sign など他にも色々な機能があるようで機会を作って試してみたい。

SDK については Node.js よりも Java の方がソースコード的にもドキュメント的にも完成度が高かったように感じた。

心残りはサーバー認証がエンタープライズ版じゃないと試せなさそうなので、仕事や覚悟が決まってエンタープライズ版を契約することがあったら試してみたい。

一旦クローズするけどまた何かしらを調べる必要が生じたらこのスクラップに追記するか新しいスクラップを作成してリンクを投稿しようと思う。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

まとめ

  • Box でアプリを登録するには開発者コンソールを使う必要がある。
  • 開発者コンソールには Box DEV というサイトからログインできる。
  • カスタムアプリの認証方法にはサーバー認証とユーザー認証の大きく 2 つがある。
  • サーバー認証はさらに JWT 使用とクライアント資格情報許可の 2 つに分けられる。
  • サーバー認証は B2B 向けで管理者が設定作業を行うケースに適している。
  • ユーザー認証は B2C 向けでユーザーが自ら設定作業を行うケースに適している。
  • サーバー認証でクライアントシークレットを取得するには 2 段階認証の設定が必要となる。
  • 無料の Individual アカウントではサーバー認証のカスタムアプリを作成しても承認できない。
  • 承認するには管理コンソールが必要で、管理コンソールには有料プラン契約が必要となる。
  • クライアント ID とシークレットはどの認証方法を使う場合でも発行される。
  • Box API Node.js SDK の TypeScript サポートはあまり良くない。
  • ファイルをダウンロードするには書き込み権限が必要となる。
  • IntelliJ IDEA で Box API Java SDK を使うには build.gradle に依存関係の追加が必要となる。
  • build.gradle では compile は非推奨なので代わりに implementation を使う。
  • IntelliJ IDEA で build.gradle の変更を反映するに Gradle ツールウィンドウの更新ボタンを押す。
  • 環境変数を設定するには Run / Debug 構成を編集する。
  • 環境変数は Git リポジトリにはコミットされない。
  • フォルダー一覧、フォルダー作成に成功した。
  • 同じ名称のファイルやフォルダーがあるとフォルダーを作成できない。
  • ファイルアップロードには分割と非分割の 2 種類がある。
  • 分割は 2 MB 以上でないと利用できない。
  • Web アプリ統合を使うと Box Web UI にさードパーティーアプリに関するアクションを組み込める。
  • Box では認可(authorization)を承認と呼ぶ。
  • Box API Java SDK には承認 URL を作成するメソッドは無いので手動で作成する必要がある。
  • 承認コードの有効期間は数秒程度と短いので承認コードとアクセストークンの交換は自動で行う必要がある。
  • 交換を自動で行うには Spring Boot で Web アプリを作ると良い。
このスクラップは2023/04/20にクローズされました