Amplify Gen2のStorage機能を使ってみた-②アップロードとリスト-

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

想像以上にボリュームがあるので、連載の形をとっています。今回はアップロードとダウンロードの機能を触っていきたいと思います。
参考:
[Amplify Gen2のStorage機能を使ってみた-①最初のアップロードまで-](https://qiita.com/asuka0708japan/items/dea93b7d8acf4a7800f6)

# アップロード
## Storage Manager React UI Componentを使う
まず以下のコマンドでライブラリをインポートします。
```terminal: Terminal
npm add @aws-amplify/ui-react-storage aws-amplify
```
早速Reactを書いて実装してみます。
アップロードボタンのコンポーネントを作成します。
```typescript: src/components/Element/uploadbutton.tsx
import { StorageManager } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';

export const DefaultStorageManagerExample = () => {
  return (
    <StorageManager
      acceptedFileTypes={['image/*']}
      path="public/"
      maxFileCount={1}
      isResumable
    />
  );
};
```
このボタンをメイン部分にインポートします。
```src/App.tsx
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import {DefaultStorageManagerExample } from '../src/components/Elements/Buttons/uploadbutton';

function App() {

  return (
    <Authenticator>
      {({ signOut, user }) => (
    <main>
      <h1> Welcome to {user?.signInDetails?.loginId}</h1>
      <button onClick={signOut}>Sign out</button>
      <DefaultStorageManagerExample/>
    </main>       
    )}
    </Authenticator>
  );
}

export default App;
```
これでボタンは生成されますが、ストレージのパスに許可を与えないとアップロードに失敗します。
ここでは以下のようにamplify/storage/resource.tsを編集します。
```typescript: amplify/storage/resource.ts
import { defineStorage } from '@aws-amplify/backend';

export const storage = defineStorage({
  name: 'amplifygen2Test',
  access: (allow) => ({
    'public/*': [
      allow.authenticated.to(['read','write']),
      allow.guest.to(['read'])
    ],
  })
});
```
これでストレージ配下のpublic/にログイン済みのユーザがファイルをアップロード出来るようになりました。

カスタムは色々可能で、詳細は下記ドキュメントにあります。
[Amplify Dev Center](https://ui.docs.amplify.aws/react/connected-components/storage/storagemanager)
多くのPropsが用意されていてカスタマイズ性が高そうです。
例えば、processFile?にファイルを処理する関数を入れると、アップロード時に処理が走ります。
displayText Propsを使えばUIの表示文字の変更も可能ですし、各種オーバーライドも可能でデザインも自由度高く変更出来そうです。今回は表示文字を日本語に変えてみます。
```diff_typescript:src/components/Element/uploadbutton.tsx
import { StorageManager } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';

export const DefaultStorageManagerExample = () => {
  return (
    <StorageManager
      acceptedFileTypes={['image/*']}
      path="public/"
      maxFileCount={1}
      isResumable
+     displayText={{
+       // some text are plain strings
+        dropFilesText: 'ドラッグ&ドロップ',
+        browseFilesText: 'ファイルを選択する',
+        uploadSuccessfulText: 'アップロード完了',
+        // others are functions that take an argument
+        getFilesUploadedText(count) {
+          return `${count}枚 画像がアップロードされました`;
+        },
+      }}
    />
  );
};
```
簡単に表示を日本語にする事が出来ました。
![画像](https://d39oe90ddnxn3x.cloudfront.net/02_amplifygen2_storage/02_amplifygen2_storage_upload_list/02_02_amplifygen2_storage_upload_list01.png)
要件に応じてまだまだ出来る事はあるので、これからも利用していきたいです。

# リスト
ドキュメントにはlist関数の使用方法が記載されています。これを利用して簡単なリストコンポーネントを実装してみます。
```diff_typescript: src/components/Lists/list.tsx
import React, { useState, useEffect } from 'react';
import { list } from 'aws-amplify/storage';

const FileList = () => {
  const [files, setFiles] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchFiles = async () => {
      try {
        const result = await list({
          path: 'public/'
        });
        setFiles(result.items); 
      } catch (err) {
        console.error(err);
        setError(err.message);
      }
    };

    fetchFiles();
  }, []);

  return (
    <div>
      <h1>File List</h1>
      {error && <p>Error fetching files: {error}</p>}
      <ul>
        {files.map((file, index) => (
          <li key={index}>
            {file.path} - Last modified: {new Date(file.lastModified).toLocaleString()}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default FileList;

```
fetchFiles 関数でS3内のpublic/配下のファイルリストを取得してこれを並べて表示します。ここではpathと最終編集日時を表示していますが、それ以外にもsizeやnextTokenといったプロパティがあります。
上記のコードで定義したFileListをApp.tsxに実装してみます。
```diff_typescript: src/App.tsx

import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import { useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";
import { generateClient } from "aws-amplify/data";
import { uploadData } from 'aws-amplify/storage';
import {DefaultStorageManagerExample } from '../src/components/Elements/Buttons/uploadbutton';
+ import FileList from '../src/components/Elements/Lists/list_test'


const client = generateClient<Schema>();

function App() {


  const [todos, setTodos] = useState<Array<Schema["Todo"]["type"]>>([]);

  useEffect(() => {
  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 (
    <Authenticator>
      {({ signOut, user }) => (
    <main>
      <h1> Welcome to {user?.signInDetails?.loginId}</h1>
    
      <button onClick={signOut}>Sign out</button>
      
      <DefaultStorageManagerExample/>  
+     <FileList/>
    </main>       
    )}
    </Authenticator>
  );
}

export default App;

```
public/にアップロードしたファイルパスをリストする事が出来ました。
![画像](https://d39oe90ddnxn3x.cloudfront.net/02_amplifygen2_storage/02_amplifygen2_storage_upload_list/02_02_amplifygen2_storage_upload_list02.png)
これで画像の表示やダウンロードに繋げる事が出来ます。

# まとめ
この記事ではドキュメントをベースに画像のアップロードとリストを実装してみました。
リストに関してはドキュメント上にsampleのコンポーネントは無かったので、この記事が少しでも初心者仲間のが参考になれば嬉しいです。次はダウンロードや削除機能を試していきます。