【入門者向け】Chat GPTに色々教えてもらいながらAWS S3にファイルをアップロードしてみる【3章: アップロード編】

# 記事の目的

- 初学者でもAmazon S3(以降S3)について理解を深められるようにする
- S3を使って画像をアップロードできるアプリケーションを作成できるようにする

**関連記事**
[1章: バケット作成編](https://qiita.com/miumi/items/58fdb6cc3809f4203ef9)
[2章: アクセス許可の設定編](https://qiita.com/miumi/items/c07cb9bf958de7c7bb2a)
[3章: アップロード編](https://qiita.com/miumi/items/017d7595c724d03c4227) ←今ここ

※この記事の内容は2章の続きからになります

# 注意事項
- AWSのコンソール画面は2023/9時点のものです
- Chat GPTの出力が必ずしも正しいとは限りませんのでビジネスで使う場合にはご注意ください。(当記事で登場しているAWSのアカウントは完全個人用になります)

# 1. ブラウザからアップロードするのはどうするのか？
早速聞いてみた。
![スクリーンショット 2023-09-16 7.28.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/fb52bc85-d337-34be-59af-f0276cb62d74.png)
![スクリーンショット 2023-09-16 7.28.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/6f6f365e-efb9-7b63-06cf-fe95a265a3e1.png)
![スクリーンショット 2023-09-16 7.28.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/e771c7c2-3c6f-d0a3-debc-d248640a0f27.png)


# 2. バックエンド環境構築
聞いてみた
![スクリーンショット 2023-09-16 7.40.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/ea2dc6bd-94e9-3372-d12b-9c581451e222.png)
![スクリーンショット 2023-09-16 7.41.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/ca78e243-5829-f23c-fdb2-38ddab515400.png)

Chat GPTのコマンド実行前に今回実装するディレクトリを作っておきましょう(ディレクトリ名は適当でOKです)
```
mkdir s3-test 
```
```
cd s3-test 
```

今のディレクトリ位置。**今後のコマンドは指定がない限りs3-test直下で実行するものとします**
```
pwd
/Users/${自分のユーザー名}/s3-test
```
必要なものをインストール
```
npm init -y
```
```
npm install express --save
```
```
npm install multer aws-sdk --save
```

前回のChat GPT出力の「プロジェクトフォルダに、前回提供したサーバーサイドのコードを作成します。コードをapp.jsなどのファイルに保存できます。」に従ってファイル作成
```
touch app.js
```

前回のChat GPT出力のバックエンドコード
```app.js
const express = require('express');
const multer = require('multer');
const AWS = require('aws-sdk');
const fs = require('fs');
const path = require('path');
const router = express.Router();

// AWS S3の設定
const s3 = new AWS.S3({
    accessKeyId: 'YOUR_ACCESS_KEY_ID',
    secretAccessKey: 'YOUR_SECRET_ACCESS_KEY'
});

// Multerの設定
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });

// ファイルアップロードのエンドポイント
router.post('/upload', upload.single('file'), (req, res) => {
    const file = req.file;

    if (!file) {
        return res.status(400).json({ success: false, message: 'ファイルがありません。' });
    }

    const params = {
        Bucket: 'YOUR_S3_BUCKET_NAME',
        Key: `${Date.now()}_${file.originalname}`,
        Body: file.buffer
    };

    s3.upload(params, (err, data) => {
        if (err) {
            console.error('S3アップロードエラー:', err);
            return res.status(500).json({ success: false, message: 'アップロードエラーが発生しました。' });
        }

        console.log('S3アップロード成功:', data.Location);
        return res.status(200).json({ success: true, message: 'アップロードが成功しました。' });
    });
});

const app = express();
app.use('/', router);

const port = process.env.PORT || 3000;
app.listen(port, () => {
    console.log(`サーバーがポート${port}で起動しました。`);
});

```

試しにサーバーを立ち上げてみましょう
```
node app.js
```
「サーバーがポート3000で起動しました。」が出れば成功です！

# フロントエンド環境構築
フロントエンドの実装に入りましょう。ディレクトリ構成を教えてば的確なアドバイスが貰えると思うのでtreeコマンドで確認

```
tree -I 'node_modules'
```

では聞いてみましょう！
![スクリーンショット 2023-09-16 8.02.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/27849f06-d3dc-1a19-91e6-11b6cf693a1a.png)
![スクリーンショット 2023-09-16 8.03.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/4d18582d-5997-7446-d3fe-e3e473aa5643.png)


```
mkdir public 
```
```
cd public
```

```
public % touch index.html
```

Chat GPT出力のフロントエンドコード
```public/index.html
<!DOCTYPE html>
<html>
<head>
    <title>画像アップロード</title>
</head>
<body>
    <input type="file" id="fileInput">
    <button onclick="uploadFile()">アップロード</button>

    <script>
        function uploadFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];

            if (file) {
                const formData = new FormData();
                formData.append('file', file);

                fetch('/upload', {
                    method: 'POST',
                    body: formData
                })
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        alert('アップロードが成功しました！');
                    } else {
                        alert('アップロードが失敗しました。');
                    }
                })
                .catch(error => {
                    console.error('アップロードエラー:', error);
                });
            } else {
                alert('ファイルを選択してください。');
            }
        }
    </script>
</body>
</html>
```

/index.htmlで表示されるようにパスを追加
```app.js
const app = express();
app.use('/', router);
app.use(express.static('public')); // 追加
```

s3-test直下で`node app.js`を実行している状態でhttp://localhost:3000/index.html にアクセス
![スクリーンショット 2023-09-16 8.19.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/947f4a9c-ddc5-93dd-29ec-43db9d8ae7ff.png)

# 4. AWSの環境変数の定義
あとは以下の部分を作成したAWSの値にすればいけると思います

```app.js
// AWS S3の設定
const s3 = new AWS.S3({
    accessKeyId: 'YOUR_ACCESS_KEY_ID',
    secretAccessKey: 'YOUR_SECRET_ACCESS_KEY'
});

// 略

 const params = {
    Bucket: 'YOUR_S3_BUCKET_NAME',
    Key: `${Date.now()}_${file.originalname}`,
    Body: file.buffer
};
```

聞いてみた
![スクリーンショット 2023-09-16 8.23.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/bf8bff8f-cf95-6463-c228-94fd3acc0a1a.png)
![スクリーンショット 2023-09-16 8.24.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/390fa904-3eae-7550-02ca-fc0e81b31985.png)

アクセスキーの設定はIAMのユーザー詳細からできそうですね！
MJというユーザーは[2章](https://qiita.com/miumi/items/c07cb9bf958de7c7bb2a#7-%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%AE%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC%E3%82%92%E8%A8%B1%E5%8F%AF%E3%81%99%E3%82%8B)で作成したユーザーです
![スクリーンショット 2023-09-16 8.26.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/31b043bb-1c32-c819-fff1-5a2e34e9a042.png)
![スクリーンショット 2023-09-16 8.26.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/996100ab-b464-0f30-442f-daa7e4e491a6.png)

アクセスキー作成をクリックしたらなんか色んな選択肢があるやん🤔
![スクリーンショット 2023-09-16 8.30.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/d38437fd-f706-b17e-76ba-a39184e4000b.png)

聞いてみた。言葉通りローカルコードが良さそう
![スクリーンショット 2023-09-16 8.33.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/dbc08fcd-590d-b84f-d0f9-0573d16a115e.png)

![スクリーンショット 2023-09-16 8.34.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/7c53e72f-a368-38d1-6a9a-09723823dd1f.png)

作成したアクセスキーを以下のコードから書き換える。
**🚨Chat GPTが何度か警告している通り、アクセスキーとシークレットアクセスキーは公開リポジトリや公共の場所にコードを公開しないように注意してください🚨**。
```app.js
// チーム開発する時はenvなどで管理するかと思われます
const s3 = new AWS.S3({
    accessKeyId: 'YOUR_ACCESS_KEY_ID', // ここ
    secretAccessKey: 'YOUR_SECRET_ACCESS_KEY' // ここ
});
```

バケット名は[1章で作成したバケット名](https://qiita.com/miumi/items/58fdb6cc3809f4203ef9#3-%E3%83%90%E3%82%B1%E3%83%83%E3%83%88%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)を入力。
**バケット名も公開リポジトリや公共の場所にコードに無闇に公開しないように注意してください**
```app.js
// チーム開発する時はenvなどで管理するかと思われます
const params = {
    Bucket: 'YOUR_S3_BUCKET_NAME', // ここ
    Key: `${Date.now()}_${file.originalname}`,
    Body: file.buffer
};
```


# 5. アップロードしてみる
適当が画像を用意してアップロードしてみる。
なんかエラー出てますね💦
![スクリーンショット 2023-09-16 8.49.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/0875a755-10eb-70a0-0f62-c717290b1869.png)

聞いてみた
![スクリーンショット 2023-09-16 8.54.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/29d78ee1-65f5-a673-8682-01674b0b14b3.png)
![スクリーンショット 2023-09-16 8.54.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/0b86d448-9a6f-7a42-7d57-0e129689b6b9.png)

どうやらCORSが原因ぽいですね(S3と関係ない範囲なので詳しい理由の調査は省略します)
```app.js
fetch('/upload', {
  method: 'POST',
  body: formData,
  mode: 'cors' // 追加
})
```
再度アップロード。いけたっぽいですね！
![スクリーンショット 2023-09-16 9.09.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/7ca08fb2-9f3c-5637-f1bf-c96d539b8903.png)

バケット詳細ページのダウンロードしてみましょう！
(日本語名だけ文字化けする可能性があるので英語にしました)
![スクリーンショット 2023-09-16 9.09.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/8104fe1f-876b-c6e7-89c6-5590a4988211.png)

画像をダウンロードできました！
![スクリーンショット 2023-09-16 9.10.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/a54cb007-b279-e670-08ab-79b683bf988c.png)

次回はブラウザから画像をダウンロードできるようにする実装を行う予定です！