Amplify Gen2のStorage機能を使ってみた-①最初のアップロードまで-

どうも、駆け出し12冠エンジニアのアスカです。Amplify Gen2がGAしてドキュメントのStorage機能に関する部分が充実していたので、それを参考にしながら触ってみます！
[Amplify Gen2のドキュメント](https://docs.amplify.aws/react/build-a-backend/storage/)

想像以上にボリュームがあったので、連載の形をとろうかと思います。今回は1つ目の最初のアップロードまでやっていきます。
1. 最初のアップロードまで
1. 認証ルールの設定
1. ファイルのアップロード
1. ファイルのダウンロード
1. ファイルプロパティの取得
1. ファイルの削除
1. ファイルのコピー
1. ファイルイベントの取得
# 事前準備
ドキュメントのQuickStartを参考にプロジェクトを作成します。
参考:[Amplify Gen2がGAしたので再Quickstartしてみた](https://qiita.com/asuka0708japan/items/ab38f719ca73e39aa93e)
Amplifyの機能部分は以下のフォルダ構成が作られます。
```
├── amplify/
│   ├── auth/
│   │   └── resource.ts
│   ├── data/
│   │   └── resource.ts
│   ├── backend.ts
│   └── package.json
├── node_modules/
├── .gitignore
├── package-lock.json
├── package.json
└── tsconfig.json
```
参考に挙げたブログで構築した環境をスタートラインとして、Storage機能を触っていきます。
## 最初のアップロードまでの流れ
こんな流れでやっていきます。
1. バックエンドストレ－ジを定義
1. ストレージへのアクセス権限を定義
1. ファイルをアップロードしてみる
### バックエンドにストレージを定義
amplify/storage/resource.tsを作成して以下のコードを記述します。
```typescript: amplify/storage/resource.ts
import { defineStorage } from '@aws-amplify/backend';

export const storage = defineStorage({
  name: 'amplifygen2Test'
});
```
ここではdefineStorage関数でストレージを定義してエクスポートしています。
name要素は好きな名前を入れてOKです。(実装はAmplifyがUUIDを付けてグローバルで一意にしてくれます)
次にamplify/backend.tsを以下のように修正して、ストレージをインポートします。すでに認証とデータベース部分は作成しているので、そこにストレージも追加する形ですね。
```diff_typescript: amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
+ import { storage } from './storage/resource';

defineBackend({
  auth,
  data,
+ storage,
});
```
### ストレージへのアクセス権限を定義
これまでのステップでストレージとしてS3バケットは作成出来ましたが、デフォルトでは誰もアクセス出来ない状態なので、defineStorageと同時にアクセス権限も定義してあげる必要があります。
ここでは以下にような設定を行います。
1. 全てのユーザは全ての写真の閲覧が可能だが、プロフィール写真をアップロードしたユーザーのみがその変更、削除が出来る。この場合、ユーザはIdentity Pool IDによって識別される。
1. 全てのユーザが写真を投稿できる一般的なプールも存在。

```diff_typescript: amplify/storage/resource.ts
import { defineStorage } from '@aws-amplify/backend';
export const storage = defineStorage({
  name: 'amplifygen2Test',
+  access: (allow) => ({
+    'profile-pictures/{entity_id}/*': [
+      allow.guest.to(['read']),
+      allow.entity('identity').to(['read', 'write', 'delete'])
+    ],
+    'picture-submissions/*': [
+      allow.authenticated.to(['read','write']),
+      allow.guest.to(['read', 'write'])
+    ],
+  })
});
```

### ファイルをアップロードしてみる
ドキュメントでは以下のコードをApp.tsxに追加し、アップロードボタンを作成しますが。ビルドで失敗しました。TypeScriptの型チェックでエラーが発生したようです。
```typescript: src/App.tsx
import React from 'react';
import { uploadData } from 'aws-amplify/storage';

function App() {
  const [file, setFile] = React.useState();

  const handleChange = (event: any) => {
    setFile(event.target.files[0]);
  };

  return (
    <div>
      <input type="file" onChange={handleChange} />
        <button
          onClick={() =>
            uploadData({
              path: `picture-submissions/${file.name}`,
              data: file,
          })
        }
      >
        Upload
      </button>
    </div>
  );
}
```
上記のコードでは、inputファイルが変更される度にhandleChangeが呼び出されてfile変数にファイルが格納され、uploadボタンを押すとuploadData関数が呼び出されてファイルがアップロードされるようになっています。ただし、このままではfileが"undefined"の可能性もあるため、TSのビルド時にエラーが発生します。以下のようにfileが"undefined"ではない事を確認してからアップロード処理する事でエラーを回避出来ます。
```typescript: src/App.tsx
import React from 'react';
import { uploadData } from 'aws-amplify/storage';

function App() {
  const [file, setFile] = useState<File | undefined>();

    const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (files) {
      setFile(files[0]);
    }
  };

  const handleUpload = () => {
    if (file) { // file が undefined でないことを確認
      uploadData({
        path: `picture-submissions/${file.name}`,
        data: file,
      })
      alert(`${file.name}がアップロードされました。`)
  } else{
    alert(`ファイルが選択されていません。`)
  }; 
};

  return (
    <div>
      <input type="file" onChange={handleChange} />
        <button
          onClick={handleUpload}>
        Upload
      </button>
    </div>
  );
}
```
一番大きな変更はhandleUpload関数を追加した部分で、関数内部でfileが"undefined"ではないか確認します。
下画像のようなアップロードボタンの作成が出来て、画像をアップロード出来る事を確認しました。
![画像](https://d39oe90ddnxn3x.cloudfront.net/02_amplifygen2_storage/01_amplifygen2_storage_firstupload/02_01_amplifygen2_firstupload_test01.png)
※ 雑にTodoアプリに追加してしまっていますが許してください笑
![画像](https://d39oe90ddnxn3x.cloudfront.net/02_amplifygen2_storage/01_amplifygen2_storage_firstupload/02_01_amplifygen2_firstupload_test02.png)


# 感想&まとめ
今回はAmplify Gen2のストレ－ジ機能をドキュメントに沿って触ってみました。
データベース同様、簡単にリソースの定義とパス毎のアクセス権限を付与する事が出来ました。色々な事がCDKベースで簡単に実現出来そうでワクワクしますね！
引き続きAmplifynGen2の機能を触っていきたいと思います！！