Unity:AWSDynamoDBから情報を貰う

<h1>AWS DynamoDBとは?</h1>
AWSのサービスに含まれている、KVS(キーバリューストア)と呼ばれるデータをキーの集合体として保存していくNoSQLデータベース。
<br>

<h1>今回試したこと</h1>
Unityから、DynamoDBにあらかじめ決まっているIDにアクセスしそこから画像URLを受け取りUnity側に画像を表示させる。
<br>主な手順は、<a href="https://qiita.com/Teru_3/items/2f764427a5df11d21ebc">こちらの記事</a>を参考にさせて頂きました。
<br>
<h2>手順</h2>
<ol>
<li>AWSアカウント作成</li>
<li><a href="#backet">S3バケットに画像をアップロード</a></li>
<li><a href="#DynamoDB">DynamoDBでテーブルを作成</a></li>
<li><a href="#Lambda">Lambda関数を作成</a></li>
<li><a href="#APIGateWay">APIゲートウェイを作成,Lambdaとリンク</a></li>
<li><a href="#Unity">Unityで画像を出す</a></li>
</ol>
ここでは、アカウントがあるものとし手順2から説明をしていきます。

<h1 name="backet">S3バケットに画像をアップロード</h1>
<h3>S3バケットの作成</h3>

サービスからS3を検索します。
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/40e5310e-38ac-ebfb-a0d0-8efbdf56d162.jpeg)

S3のバケットを選択し、[バケットを作成]を押します。
![キャプチャ2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/4ccfa3f2-8f0a-ce66-dcc0-97445f6889d2.jpeg)

お好きなバケット名を入力し、お好きなAWSリージョンを設定します。リージョンの地域設定は右上[グローバル]から設定を変更できます。
![キャプチャ3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/a9512797-fd59-8805-3895-cd75a7209af9.jpeg)

次に[このバケットのブロックパブリックアクセス設定]の、[パブリックアクセスを全てブロック]のチェックを外し、下の警告のチェックボックスにチェックを入れます。
![キャプチャ4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/bd1c0c87-5dcd-ea25-2422-e6f62f637f5a.jpeg)

上記が完了したら、ページ下部にある[バケットを作成]を押して下さい。
<br>
<h3>パブリックの設定</h3>

作成されたバケットは最初のバケットメニューに先ほど付けた名前のバケット(私の場合tipsbacket)が表示されます。
名前の部分をクリックしバケット毎の設定メニューに移動します。
![キャプチャ5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/7ff384af-2026-a8f2-178f-dcfe6cf9344b.jpeg)

移動が完了したら、[アクセス許可]を選択し下の[バケットポリシー]の[編集]を押します。
![キャプチャ6.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/0489dd06-2a0c-b561-4006-522d0dc8a900.jpeg)

下記のコードをポリシーに書き込み[変更の保存]をします。

```json:ポリシー
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "PublicReadGetObject",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::作成したバケットの名前/*"
		}
	]
}
```
![キャプチャ7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/a52f246e-1962-2fce-83f1-a72f1f030f09.jpeg)
<h3>画像のアップロード</h3>

ポリシーの変更が保存出来たら、先ほどのバケット毎の設定の[オブジェクト]を選択し、[アップロード]を押すか、アップロードしたいものをこの画面でドラッグ＆ドロップすることでアップロード確認画面へ移動します。
![キャプチャ8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/0d3e54b8-db7c-e849-f983-c8fc972ae64e.jpeg)

先ほど、ドラッグ＆ドロップで移動した人は[ファイルとフォルダ]には選択したものが表示されています。この画面からアップロードする場合は、[ファイルの追加][フォルダの追加]または、ドラッグ＆ドロップで選択できます。アップロードしたいものを選択し終えたら[アップロード]を押します。
![キャプチャ9.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/4faec1f7-1579-a042-bf2a-870c290f3355.jpeg)

アップロードが完了したら、オブジェクトメニューにアップロードしたものの名前が表示されているのでクリックします。
下記画像のような画面が表示されたら、右下オブジェクトURLをクリックします。
アップロードしたものが表示されたら成功です。
![キャプチャ10.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/f93ae8ee-058e-3b07-3eb2-1e19daffb747.jpeg)
![キャプチャ11.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/d2c77d7c-3c0c-cc2f-5253-c84e206ff5e3.jpeg)

以上で、S3については終了です。

<h1 name="DynamoDB">DynamoDBでテーブルを作成</h1>

サービスからDynamoDBを検索し、さっそく[テーブルの作成]をしていきましょう。
![キャプチャ12.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/a750ec55-8067-4f36-efda-c13855494ae8.jpeg)

お好きな[テーブル名]と、[パーテンションキー]の名前を決めます。
[パーテンションキー]は項目検索の際に必ず必要なので、今回は分かりやすく「ID」と入力しましょう。
[ソフトキー]とその他の設定は今回は使用しないのでそのままにして、ページ下の[テーブルの作成]を押してください。
![キャプチャ13.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/05eb0c41-9381-c3fb-baa0-75f268e4c3f3.jpeg)

テーブルの作成が出来たら、左のDynamoDBメニューの[テーブル]→[項目の探索]を選択します。
下記の画像の様な画面になったら、[返された項目]にある[項目を作成]を押します。
![キャプチャ14.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/768dfd0d-5d41-1ee3-c7f1-8f8805f288c1.jpeg)

そうしたら、[パーテンションキー]であるIDを[値]に入力。数字で1~入力すると後から楽です。
その後[新しい属性の追加]から文字列を選択し、[属性名]を「URL」、[値]に先ほどS3にアップロードしたもののURLを入力して、[項目を作成]を押してください。
![キャプチャ15.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/705f0b33-00a3-54bb-b554-295299d4beca.jpeg)

項目が追加されていたら成功です。
![キャプチャ16.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/ee56d2b9-1ebe-ead7-1333-f13fe9d688f7.jpeg)

DynamoDBでのテーブル作成は以上です。
<br>
<h1 id="Lambda">Lambda関数を作成</h1>

<h3>IAMでロールの作成</h3>

まずは、LambdaがDynamoDBにアクセスできるようにロールを設定します。
サービスからIAMを検索します。
AIMの左のメニューから[アクセス管理]→[ロール]を選択し、[ロールを作成]を押します。
![キャプチャ17.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/a62ee266-2eb6-0a26-1e82-8891897fc270.jpeg)

画面が移動したら、エンティティタイプを[AWSのサービス]、ユースケースを[Lambda]を選択して[次へ]を押してください。
![キャプチャ18.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/7ea9e522-540b-389d-e7e6-9d96d675de90.jpeg)

次に、許可ポリシーを選択します。
[CloudWatchFullAccess]と[AmazonDynamoDBFullAccess]にチェックを入れ[次へ]を押します。似た項目が多いので検索をかけるとより早く見つかります。
![キャプチャ19.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/d20a8729-edbf-9bda-2f78-8cd3a13e5696.jpeg)
![キャプチャ20.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/df85f4e0-8d1e-abc9-4e8a-464de4aa8a9c.jpeg)

最後にロールの名前を決めてページ下部の[ロールを作成]から作成を完了してください。
![キャプチャ21.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/ffe5ac8e-a9c3-e6ea-4067-ad04ba8e5edc.jpeg)

<h3>Lambdaの作成、設定</h3>

サービスからLambdaを検索します。
Lambdaのメニューが開けたら、[関数の作成]を押します。
![キャプチャ22.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/e45626d6-5b88-e36d-609a-ddf4e627511a.jpeg)

関数の作成で、[一から作成]を選択し、お好きな[関数名]を入力します。
[ランタイム]は今回、「Node.js 16.x」を使用します。
これらを選択したら[関数の作成]を押します。
![キャプチャ23.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/145e20a2-681d-702f-8e37-f05bc4b60e13.jpeg)

関数の作成が終って開かれるメニューに[コード]というものがあるのでそれを選択し下記のコードを記入し、[Test]の横にある[Deploy]を押し変更を保存します。
![キャプチャ24.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/2995647a-6dcf-0d7d-6490-aa493ca89384.jpeg)

```javascript:index.js
const AWS = require('aws-sdk')
const client = new AWS.DynamoDB.DocumentClient()

/**
 * API Gatewayを通してリクエストから受け取ったIDと一致するitemを取得して返却するLambda
 * 
 * @param {*} event APIGatwayからわたってくるパラメーターがこのevent引数を通して渡される
 * GETリクエストにおけるクエリはqueryStringParametersというプロパティに設定されている
 * @returns 
 */
exports.handler = async (event)=>{

    console.log("event", event);
    const ID = event.Keys.ID;

    /**
     * ハッシュキー:IDというDynamoDBテーブルを想定
     */
    const result = await client.query({
        TableName:'TestDB',//テーブル名
        KeyConditionExpression:'#ID = :ID',
        ExpressionAttributeNames:{
            '#ID':'ID'
        },
        ExpressionAttributeValues:{
            ':ID':ID
        }
    }).promise()


    const items = result.Items

    return {
        statusCode:200,
        body:items
    }
    
}
```

[Deploy]を押して変更を保存が確認出来たら。[コード]と同じメニュー欄にある[設定]を選択して、[アクセス権限]を選択します。
アクセス権限のメニューから[実行ロール]の[編集]を押してください。
![キャプチャ25.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/a8936a13-1e37-cfe8-5123-4e4ff93ef297.jpeg)

画像のような画面が出たら、[既存のロール]から先ほど作成したロールを選択し[保存]してください。[説明]は任意で入力してもいいです。
![キャプチャ26.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/ddde70ef-ef56-960a-dfe0-a1aaa1b1db03.jpeg)

Lambdaの設定は以上です。
<br>
<h1 name="APIGateWay">APIゲートウェイを作成,Lambdaとリンク</h1>

サービスからAPI Gatewayを検索し、画像の様な画面が出たら[APIを作成]を押します。
![キャプチャ27.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/5a1f250f-871a-35d5-0a8a-5736677b15e5.jpeg)

次の画面で、画面下部の[REST API]の[構築]を押してください。
![キャプチャ28.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/3b5b3f54-1879-8528-d2b0-65fe1db6b010.jpeg)

その次の画面では、[API name]を入力して[create API]を押してください。
[Description]は任意で入力してください。
![キャプチャ29.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/5b374bf8-bb40-6594-67b1-94d73d1a628f.jpeg)

APIが作成されたら、[Actions]の中から[Create Method]を選択し、現れた空白のプルダウンメニューから[POST]を選択してチェックマークを押してください。
![キャプチャ30.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/1e85a776-900a-c126-d658-41cca80d2a65.jpeg)
![キャプチャ31.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/a265b5b0-602a-c0a4-36f4-5ba2b883d17d.jpeg)

そうすると右に画像の様なメニューが表示されるので、[Lambda Function]の欄に先ほど作成したLambda関数の名前を入力して[Save]を押してください。Lambdaに権限を追加するかを聞かれるので[OK]を押します。
![キャプチャ32.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/784f1c06-aaad-4b5e-39a3-b51dc2e98ae6.jpeg)
![キャプチャ33.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/e363aa1b-9a58-d6a1-af77-35f5e66f7a46.jpeg)

次に[Actions]から、[Enable CORS]を選択します。選択すると右側にメニューが表示されるので、そのまま[Enable CORS and replace CORS headers]を押します。そうすると、メソッドの変更の確認をしてくるので、[Yes,replace existing values]を押します。
![キャプチャ34.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/1b7b83fa-3789-ea9f-3f66-f08505089792.jpeg)
![キャプチャ35.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/bd4c08d6-262b-9a14-03c9-15029d4d48c9.jpeg)
![キャプチャ36.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/b5b1279d-d155-be81-d09b-53d799eb3188.jpeg)

メソッドの変更が完了したら、再び[Actions]を開き[Deploy API]を選択します。
ポップアップ表示されたメニューの[Deployment stage]で[New Stage]を選択し、[Stage name]にお好きな名前を付けて[Deploy]を押します。
![キャプチャ37.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/e8a97b7e-1a3c-396d-fd47-332416ed2528.jpeg)
![キャプチャ38.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/b1afaa30-c1b9-54b1-b1c4-311e649a7e45.jpeg)

デプロイが終わると、APIゲートウェイに接続するために必要な「Invoke URL」が表示されるのでこれをUnityで使います。
![キャプチャ39.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/0f50138b-bb61-fcd5-fc09-a1617e1aa181.jpeg)

AWSでのあれこれはこれで終了です。
<br>
<h1 id="Unity">Unityで画像を出す</h1>

Unityでプロジェクトを作成し以下の準備します。

<ul>
<li>Rawimageの作成</li>
<li>空のオブジェクトの作成</li>
<li>C#スクリプトを2つ作成</li>
</ui>

私は、画像を表示させるものをManager.cs
APIゲートウェイにアクセスするものをUnitytoaws.cstとしました。

スクリプトはこちらです。

```c#:Manager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;

public class Manager : MonoBehaviour
{
    public List<string> URLs = new List<string>();
    [SerializeField] private RawImage _image;

    [System.Obsolete]
    public IEnumerator  Indicate()
    {
        UnityWebRequest www = UnityWebRequestTexture.GetTexture(URLs[0]);

        //画像を取得できるまで待つ
        yield return www.SendWebRequest();

        if (www.isNetworkError || www.isHttpError)
        {
            Debug.Log(www.error);
        }
        else
        {
            //取得した画像のテクスチャをRawImageのテクスチャに張り付ける
            _image.texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
            _image.SetNativeSize();
        }
    }
}
```
<br>

```c#:Unitytoaws.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
public class Unitytoaws : MonoBehaviour
{
    [SerializeField] private string ID;//欲しい情報のID
    [SerializeField] private DynamoResponse response;//返ってきたものを表示する
    [SerializeField] Manager Manager;

    public string URL;//URLを入れるよう

    [Serializable]
    public class DynamoIftest
    {
        public DynamoQueryKey Keys;
        [Serializable]
        public class DynamoQueryKey
        {
            public string ID;//クエリに使うキー
        }
    }

    [Serializable]
    public class DynamoResponse
    {
        public List<DynamoResponseItems> body;
        [Serializable]
        public class DynamoResponseItems
        {
            public string ID;//取得ID
            public string URL;//取得URL
        }
    }

    [Obsolete]
    void Start()
    {
        StartCoroutine(AwsApiTest());
    }

    [Obsolete]
    private IEnumerator AwsApiTest()
    {
        var awsElement = new DynamoIftest()
        {
            Keys = new DynamoIftest.DynamoQueryKey() { ID = ID}
        };
        string jsonStr = JsonUtility.ToJson(awsElement);

        var request = new UnityWebRequest();

        request.url = "APIゲートウェイの";//APIゲートウェイとの通信
        var body = Encoding.UTF8.GetBytes(jsonStr);
        request.uploadHandler = new UploadHandlerRaw(body);
        request.downloadHandler = new DownloadHandlerBuffer();

        request.SetRequestHeader("Content-Type", "application/json");
        request.method = UnityWebRequest.kHttpVerbPOST;
        yield return request.Send();

        if (request.isNetworkError)
        {
            Debug.Log(request.error);
        }
        else
        {
            if (request.responseCode == 200)
            {
                var responseJsonText = request.downloadHandler.text;
                Debug.Log(responseJsonText);
                response = JsonUtility.FromJson<DynamoResponse>(responseJsonText);
                Debug.Log(response.body[0].URL);
                URL = response.body[0].URL;

                Manager.URLs.Clear();
                foreach (var item in response.body)
                {
                    Manager.URLs.Add(item.URL);
                }
                Manager.StartCoroutine("Indicate");
            }
            else
            {
                Debug.Log("failed");
            }
        }
    }
}

```

スクリプトが書けたら、RawimageにManaget.csをドラッグ＆ドロップしコンポーネントを追加します。
![キャプチャ40.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/260b7ee8-a43c-4c58-f6e9-b50eedfb60f6.jpeg)

次に空のオブジェクトにUnitytoaws.csをドラッグ＆ドロップしコンポーネントを追加します。そして、インスペクターの[ID]にDynamoDBから取り出したい項目のIDを入力します。
![キャプチャ41.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/9c6946ec-8676-2993-2f47-0dccdc74e6d2.jpeg)

実行すると、UnityがAPIゲートウェイへアクセスし、それをトリガーとしたLambdaが動きDynamoDBからURLを取得しそのURLでS3に保存されている画像を表示することが出来ました！！
![キャプチャ42.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3225110/ab47c095-195b-10f2-9d8a-ef5a59010522.jpeg)

Git:https://github.com/nkc20206082/AWSTest.git

<h1>今回のまとめ</h1>
今回、AWSを全く知らない私がネットの記事を参考にしつつここまで作るとが出来ました。最初は、AWSでサーバーを借りてPHPを使ってMySQLでやろうと思っていたことが直ぐに出来る便利なサービスだと思いました。しかし、DynamoDBはKVSを特徴とするDBだった為自分の知識にあるものと相違があり思ったような実装も難しいことが分かりました。
しかし、今回初めてNoSQLなDBを触って複雑ではない情報を扱う場合こちらの方が使いやすいことは理解しました。
次は、こちらも途中で断念したAWSでサーバーを借りDockerを構築してPHPの実行環境を作ることをやっていきたいと思います。