UnityからBOXへ画像を保存し、保存先のURLを取得する

## やりたいこと

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/32e73949-b406-0f4f-a605-79eace2bc9f8.png)

## 今回やったこと

BOXSDKをUnityへインストールし、開発者トークンでタイトルのフローを仮実装しました。

## サンプルプロジェクト
https://github.com/Atsu-sys/BoxImageUploadSample.git

## BOX v2 SDKをUnityにインストールする

BOX v2 SDKをインストールするにはNuGetからパッケージのインストールをする必要があるため、UntiyからNuGetパッケージをインストールできる環境を作ります。

### NuGet For Unityのインストール
OpenUPMからインストールできます。
※OpenUPMの使い方は今回は省略

```
URL : https://package.openupm.com
Scope : com.github-glitchenzo.nugetforunity
```

:::note warn
Unity NuGetからSDKをインストールすることはできなかったので注意
:::

## NuGetからBoxSDKをインストール

### Manage NuGet Packagesを開く

タスクバーにNuGetが追加されるので、
Manage NuGet Packagesを開きます。

![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/ff1d99ae-e179-bb8f-66e4-679f19d513a3.png)

### Box.v2をインストール
・ Box.v2
・ Box.v2.Core
をインストールします。

![スクリーンショット 2024-08-22 185615.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/79bbc49d-814f-f47c-23d5-7777be77f545.png)

## Boxへ画像をアップロードする

### BoxUploaderクラスを作成
Boxとの通信部分を担うピュアC#クラスを作成します。
``` BoxUploader.cs
using Box.V2;
using Box.V2.Config;
using Box.V2.Models;
using Box.V2.Auth;
using Box.V2.Exceptions;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;

public class BoxUploader
{
    private BoxClient _client;

    // コンストラクタ：BoxClientを初期化
    public BoxUploader(string clientId, string clientSecret, string developerToken)
    {
        try
        {
            // BoxClientの設定を作成
            var config = new BoxConfigBuilder(clientId, clientSecret).Build();
            // セッションを作成
            var session = new OAuthSession(developerToken, "refresh-token", 3600, "bearer");
            // BoxClientを初期化
            _client = new BoxClient(config, session);

            if (_client == null)
            {
                Debug.LogError("BoxClientの作成に失敗しました。");
            }
        }
        catch (System.Exception ex)
        {
            Debug.LogError("BoxUploaderの初期化に失敗しました: " + ex.Message);
        }
    }

    // 画像をBoxにアップロードし、共有リンクを取得する非同期メソッド
    public async Task<string> UploadImageAsync(string filePath, string folderId = "0")
    {
        try
        {
            // BoxClientが初期化されているか確認
            if (_client == null)
            {
                throw new System.Exception("BoxClientが初期化されていません。");
            }

            // FilesManagerが初期化されているか確認
            if (_client.FilesManager == null)
            {
                throw new System.Exception("FilesManagerが初期化されていません。");
            }

            // 指定されたファイルパスが存在するか確認
            if (!File.Exists(filePath))
            {
                throw new System.Exception("ファイルパスにファイルが存在しません: " + filePath);
            }

            using (var fileStream = new FileStream(filePath, FileMode.Open))
            {
                // ファイルをBoxにアップロード
                var boxFile = await _client.FilesManager.UploadAsync(new BoxFileRequest
                {
                    Name = Path.GetFileName(filePath),
                    Parent = new BoxRequestEntity { Id = folderId }
                }, fileStream);

                if (boxFile == null)
                {
                    throw new System.Exception("ファイルのアップロードに失敗しました。BoxFileがnullです。");
                }

                // 共有リンクがない場合、新たに作成
                if (boxFile.SharedLink == null)
                {
                    var sharedLinkRequest = new BoxSharedLinkRequest
                    {
                        Access = BoxSharedLinkAccessType.open
                    };

                    boxFile = await _client.FilesManager.CreateSharedLinkAsync(boxFile.Id, sharedLinkRequest);

                    if (boxFile.SharedLink == null)
                    {
                        throw new System.Exception("アップロードされたファイルの共有リンク作成に失敗しました。");
                    }
                }

                // 共有リンクのURLを返す
                return boxFile.SharedLink.Url;
            }
        }
        catch (BoxAPIException apiEx)
        {
            // Box APIからの例外をキャッチ
            Debug.LogError($"Box APIエラー: {apiEx.Message}");
            Debug.LogError($"ステータスコード: {apiEx.StatusCode}");
            Debug.LogError($"レスポンスヘッダー: {apiEx.ResponseHeaders}");
            throw;
        }
        catch (System.Exception ex)
        {
            // その他の例外をキャッチ
            Debug.LogError($"UploadImageAsyncに失敗しました: {ex.Message}");
            throw;
        }
    }
}
```
### ScreenshotHandlerクラスを作成
シーン内に置くMonoBehaviorを継承したクラスを作成し、
UIボタンのOnClickイベントにCaptureAndUploadScreenshotメソッドをフックさせます。
```ScreenshotHandler.cs
using System.IO;
using UnityEngine;
using System.Threading.Tasks;

public class ScreenshotHandler : MonoBehaviour
{
    [SerializeField] private string clientId = "";
    [SerializeField] private string clientSecret = "";
    [SerializeField] private string developerToken = "";
    private BoxUploader boxUploader;

    private void Start()
    {
        // BoxUploaderの初期化（適切なクライアントID、クライアントシークレット、デベロッパートークンを設定）
        boxUploader = new BoxUploader(clientId, clientSecret, developerToken);
    }

    public async void CaptureAndUploadScreenshot()
    {
        // 現在の日付と時刻を取得し、ファイル名に使用
        string timestamp = System.DateTime.Now.ToString("yyyyMMdd_HHmmss");
        string fileName = "Screenshot_" + timestamp + ".png";

        // パスを構築
        string path = Path.Combine(Application.persistentDataPath, fileName);

        // スクリーンショットを保存
        ScreenCapture.CaptureScreenshot(path);
        Debug.Log("Screenshot saved at: " + path);

        // 保存が完了するまで少し待機（スクリーンショットが保存される時間）
        await Task.Delay(500);  // 0.5秒待機

        // スクリーンショットをBoxにアップロード
        try
        {
            string url = await boxUploader.UploadImageAsync(path);
            Debug.Log("Uploaded file URL: " + url);
        }
        catch (System.Exception ex)
        {
            Debug.LogError("Failed to upload screenshot: " + ex.Message);
        }
    }
}

```

Boxの開発者コンソールからマイアプリを作成し、
・開発者トークン
・クライアントID
・クライアントシークレット 
を取得します。
それをインスペクターから入力してください。
インスペクターの構成は以下のような感じです。
![スクリーンショット 2024-08-23 184058.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/521191cc-e16f-d63f-fd9c-eebb5de16b4c.png)

## 画像を送信し、URLを取得

シーン内のボタンをクリックすると画像がBOXへ送信されて、URLが返ってきます。

:::note warn
60分で開発者トークンが使えなくなるので注意
:::

## 次回
OAuth 2.0を使用してアクセストークンを取得し、それを使用することでトークンの期限切れを回避できるようにするところを次回行いたいと思っています。