
AWS Amplify + ReactでS3オブジェクトのカスタムメタデータを読み書きする
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS Amplify + React で S3オブジェクトのカスタムデータの読み書きをしてみました。
準備
React Appを作成する
アプリ名をmy-s3-controllerとしました。
$ npx create-react-app my-s3-controller $ cd my-s3-controller
Amplifyをセットアップする
$ amplify init ? Enter a name for the environment dev ? Choose your default editor: Vim (via Terminal, macOS only) ? Choose the type of app that you're building javascript Please tell us about your project ? What javascript framework are you using react ? Source Directory Path: src ? Distribution Directory Path: build ? Build Command: npm run-script build ? Start Command: npm run-script start Using default provider awscloudformation ? Select the authentication method you want to use: AWS profile ? Please choose the profile you want to use [select your profile] $ npm install aws-amplify @aws-amplify/ui-react
Amplify Authモジュールを追加する
Amplify Authモジュールを追加します。 ここではユーザーはEmailでログインする設定としました。
$ amplify auth add Do you want to use the default authentication and security configuration? Default configuration How do you want users to be able to sign in? Email Do you want to configure advanced settings? No, I am done. $ amplify push
Cognito ユーザープールが作成されます。
AWSコンソールで、作成されたユーザープールのプール IDと(*_app_clientWebのID)をメモし、IDプールのCognitoの接続設定に入力して、Cognito IDプールを作成します。

Cognito IDプール作成時にロールも作成することになりますが(アプリ名がmy-s3-controllerなのでデフォルトではロール名はCognito_MyS3ControllerAuth_Roleになります)、このロールにs3:ListBucket、s3:GetObject、s3:PutObjectを与えておきまます。
既存のS3バケットに接続する
今回は既存のS3バケットを使いたかったので、src/index.jsに下記の修正を行います。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
+ import Amplify from 'aws-amplify';
+ import awsExports from './aws-exports';
+ Amplify.configure(awsExports);
+
+ Amplify.configure({
+ Auth: {
+ identityPoolId: process.env.REACT_APP_COGNITO_IDENTITY_POOL_ID,
+ region: process.env.REACT_APP_COGNITO_REGION
+ },
+ Storage: {
+ AWSS3: {
+ bucket: process.env.REACT_APP_S3_BUCKET,
+ region: process.env.REACT_APP_S3_REGION
+ }
+ }
+ });
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
.envファイルを下記のように作ります。
REACT_APP_COGNITO_IDENTITY_POOL_ID=「接続するCognito IDプールのプールID REACT_APP_COGNITO_REGION=[接続するCognitoIDプールのリージョン] REACT_APP_S3_BUCKET=[接続するS3バケット名] REACT_APP_S3_REGION=[接続するS3バケットのリージョン]
ここで、一旦、npm startしてhttp://localhost:3000でReactのデフォルト画面が表示されることを確認しておきます。
$ npm start

接続するS3バケットのCORS設定を行う
http://localhost:3000で稼働するアプリからS3バケットにオブジェクト(ファイル)を作るためにはCORS(Cross-Origin Resource Sharing)の設定が必要になります。
下記のようにS3バケットのCORSを設定します。
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"HEAD",
"GET",
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"http://localhost:3000"
],
"ExposeHeaders": [
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2",
"ETag",
"x-amz-meta-foo"
],
"MaxAgeSeconds": 3000
}
]
AllowedOriginsにCORSを許可するアプリのURL(http://localhost:3000)を指定します。
また、カスタムメタデータfooを読み書きしたいので、ExposeHeadersにx-amz-meta-fooを追加しています。この設定を行なっておかないと、当該カスタムメタデータの読み込みができません(書き込みは設定していなくてもできます)。nameというカスタムメタデータを読み込みたい場合はx-amz-meta-nameを追加する必要があります。
認証ユーザを作る
src/App.jsを下記に修正してCognitoの認証を通してアプリを使うように変更します。
import logo from './logo.svg';
import './App.css';
+ import React from 'react'
+ import { Authenticator } from '@aws-amplify/ui-react';
+ import '@aws-amplify/ui-react/styles.css';
- function App() {
+ const App = () => {
return (
- <div className="App">
- <header className="App-header">
- <img src={logo} className="App-logo" alt="logo" />
- <p>
- Edit <code>src/App.js</code> and save to reload.
- </p>
- <a
- className="App-link"
- href="https://reactjs.org"
- target="_blank"
- rel="noopener noreferrer"
- >
- Learn React
- </a>
- </header>
- </div>
- );
+ <Authenticator>
+ {({ signOut, user }) => (
+ <div className="App">
+ <header className="App-header">
+ <img src={logo} className="App-logo" alt="logo" />
+ <p>
+ Edit <code>src/App.js</code> and save to reload.
+ </p>
+ <a
+ className="App-link"
+ href="https://reactjs.org"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ Learn React
+ </a>
+ </header>
+ </div>
+ )}
+ </Authenticator>
+ )
}
- export default App;
+ export default App
npm startして、アプリにアクセスするとCognitoの認証画面が表示されます。
アプリを利用するために、Create Accountタブからユーザーを作成します。

書き込み
AmplifyのStorage.putメソッドを使うことでS3にオブジェクトを作成できます。
この際に、カスタムメタデータはmetadataキーで指定します。カスタムメタデータfooの値に日本語を使えるようにencodeURIComponentでエンコードします。
const uploadFile = async(file, foo) => {
if (!foo) return
if (!file) return
const fileName = Date.now() + '.' + file.type.replace(/(.*)\//g, '')
const result = await Storage.put(fileName, file, {
level: 'protected',
contentType: file.type,
metadata: { foo: encodeURIComponent(foo) }
})
if (result) {
console.log(result)
} else {
console.error(result.error)
}
uploadFileはS3にアップロードするファイルのfileオブジェクトとfooの値を受け取ってStorage.putを呼び出しています。 また、levelにprotectedを指定して他のユーザがアップしたファイルも読み取れる指定をしています(ただし、identityIdも指定しないと、protectedの場合は他のユーザがアップしたファイルは読み取れません)。
Storage - Upload files - JavaScript - AWS Amplify Docs
読み込み
S3にアップロードしたオブジェクトの読み込みにはAmplifyではStorage.getメソッドやStorage.listメソッド提供されています。しかし、これらのメソッドでカスタムメタデータを読み出すことは2022/05/01現在できません。したがって、カスタムメタデータを読み出したい場合は、S3 Client - AWS SDK for JavaScript v3を使う必要があります。
AWS SDKをインストールします。
$ npm install aws-sdk
カスタムメタデータの読み出しは次のように行います。
import React from 'react'
import { Auth } from 'aws-amplify'
import { S3Client, HeadObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3'
const fetchS3Objects = async (bucket) => {
try {
const s3client = new S3Client({
region: process.env.REACT_APP_S3_REGION,
credentials: await Auth.currentCredentials()
})
const output = await s3client.send(
new ListObjectsV2Command({
Bucket: bucket
})
)
if (!output.Contents) return []
const heads = []
for (let i =0; i < output.Contents.length; i++) {
const c = output.Contents[i]
const head = await s3client.send(
new HeadObjectCommand({
Bucket: bucket,
Key: c.Key
})
)
heads.push({ foo: decodeURIComponent(head.Metadata.foo) })
}
return heads
} catch (err) {
console.error(err)
}
}
ListObjectsV2Commandメソッドでバケット内にあるオブジェクトの一覧情報を得ます。ここには各オブジェクトのKeyが含まれているので、さらに各Keyに対して、HeadObjectCommandメソッドを呼び出しカスタムメタデータをhead.Metadata.fooで取得しています。
ここで気を付ける必要があるのは、ListObjectsV2Commandは{ level: 'protected' }で制限をかけたオブジェクトだけではなく、バケット内の全オブジェクトを取得するということです。ですので、出力を{ level: 'protected' }のオブジェクトに限定したい場合は、別途Storage.listメソットなどでprotectedに限定された一覧を取得して、ListObjectsV2Commandで得たカスタムメタデータをマージする必要があります。
まとめ
AWS Amplify + ReactでS3オブジェクトのカスタムメタデータを読み書きする方法に関して記述しました。
カスタムメタデータの書き込みはシンプルなのですが、読み込みはCORSのヘッダの設定やaws-sdkの利用など実現には一工夫が必要でした。
参考になれば幸いです。







