[AWS S3]LaravelアプリケーションでS3に画像を保存する ~備忘録~

## はじめに
この記事はプログラミング初学者による備忘録用の記事であり、また、少しでも他の初学者のお役に立てればと思い書いています。

今回は、Laravelアプリケーションの画像保存先をS3に設定しましたので、備忘録としてまとめておきたいと思います。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。

## S3とは
Amazon S3は、拡張可能性やデータ可用性を提供するオブジェクトストレージサービスであり、様々なユースケースをサポートしつつコスト効率を高め、セキュリティを強化してくれます。
>Amazon Simple Storage Service (Amazon S3) は、業界をリードするスケーラビリティ、データ可用性、セキュリティ、およびパフォーマンスを提供するオブジェクトストレージサービスです。あらゆる規模や業種のお客様が、データレイク、クラウドネイティブアプリケーション、モバイルアプリケーションなど、事実上あらゆるユースケースで、あらゆる量のデータを保存、保護することができます。
``引用:``[Amazon S3](https://aws.amazon.com/jp/s3/)

詳細は[リンク先](https://aws.amazon.com/jp/s3/?nc=sn&loc=0)をご確認下さい。

### ・S3を利用する理由
アプレケーション上の画像(静的コンテンツ)の保存場所を、WebサーバーではなくS3にすることで下記のようなメリットが得られます。

・webサーバーのストレージに影響を与えなくて済む。

・HTMLへのアクセスと画像へのアクセスを分けることができ、結果的に負荷を分散することができる。
**->** もし、webサーバー上に画像が保存されていると、画像へのアクセスの際にwebサーバーにアクセスする必要があり、webサーバーの負荷が増えます。

・画像の保存場所をS3に分離することで、webサーバーの台数を簡単に増設することができる。
**->** webサーバー上に画像が保存されていると、webサーバーを増設する際に、画像を同期する必要が出てきます。

・CloudFrontを使用すると、画像配信を高速化できる。

### ・S3の基礎概念
``バケット``:　オブジェクトの保存場所であり、バケット名はグローバルでユニークな必要があります。

``オブジェクト``:　データ本体のことを指し、S3に格納されるファイルでURLが付与されています。(バケット内でのオブジェクト数は無制限)

``キー``:　オブジェクトの格納URLパスのことを指します。

## S3環境の作成手順
今回は、Laravelアプリケーションで画像保存を行う際のデータ保存先をS3にします。

### ・1.S3のバケットを作成する
**・一般的な設定**

``バケット名``: グローバルで一意な名前を付ける必要があります。

``AWSリージョン``: 対象となるアプリケーションのEC2インスタンスと同じリージョンを選択します。

``既存のパケットから設定をコピー``: オリジナルの設定を行う場合は選択する必要はありません。


![スクリーンショット 2022-03-07 22.37.59（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/836a8767-83c2-711f-3e9d-483b8845dbe4.png)

**・オブジェクト所有者**
他の AWS アカウントからこのバケットに書き込まれたオブジェクトの所有権と、アクセスコントロールリスト (ACL) の使用を管理します。
オブジェクトの所有権は、オブジェクトへのアクセスを指定できるユーザーを決定します。

他のAWSアカウントからのアクセスを許可する際は、ACLを有効化してください。

![スクリーンショット 2022-03-07 22.39.55（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/f5950d7e-9e37-5818-096e-9d4a2cd086b5.png)

**・バケットのブロックパブリックアクセス設定**
この設定では、バケットとオブジェクトに対する外部からのアクセスを禁止するための設定を行います。

今回は、インターネットに配信する画像を保存するため、設定はオフにしています。(ユースケースに合わせて設定を行って下さい)

![スクリーンショット 2022-03-07 22.38.19（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/219b89d6-624a-81fa-a44a-354bcb85eb2b.png)

**・その他の設定**

``バケットのバージョニング``:
バージョニングとは、同じバケット内でオブジェクトの複数のバリアントを保持する手段のことです。要するに、同じファイル名を保存する際は各ファイルに一意なバージョンIDを付けて保存してくれます。
S3のバージョニング機能を使用すると、バケットに保存されたすべてのオブジェクトのバージョンを、保存、取得、復元することができます。

バージョニングは、**バケットレベルで有効化および停止**します。
バージョニング状態は、バケット内の一部ではなくすべてのオブジェクトに適用されるので、バージョニングを有効にした際は、全ての新しいオブジェクトがバージョニングされ**一意のバージョンID**が割り当てられます。

<font color=red>※1度バケットのバージョニングを有効にすると、バージョニング無効の状態に戻すことはできません。</font>
<br>
``デフォルトの暗号化``:
S3バケットでデフォルトの暗号化を有効にすることで、新しいオブジェクトがバケットに保存される際に暗号化されるようにできます。
詳細は、[リンク先](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/bucket-encryption.html)をご確認下さい。
<br>
``オブジェクトロック``
オブジェクトロックにより、オブジェクトが削除または上書きされることを、一定期間または無期限に防止できます。
詳細は、[リンク先](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/object-lock.html)をご確認下さい。
![スクリーンショット 2022-03-07 22.38.44（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/611b6720-9712-288e-39d8-09bf7239244e.png)

### ・2.S3を操作する権限を持つIAMユーザーを作成する
**・ユーザーを追加**
``ユーザー名``: グローバルで一意なユーザー名を入力して下さい。

``AWS アクセスの種類を選択``: 今回は、LaravelからS3にアクセスするのでプログラムによるアクセスを選択しています。

![スクリーンショット 2022-03-08 10.45.51（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/5ceb027f-697e-5aa0-b478-fbc563df181d.png)

**・アクセス許可の設定**
S3で検索して、適切なアクセス権限を選択して下さい。

![スクリーンショット 2022-03-08 10.46.50（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/1c0ab265-f382-9e1d-1baf-51c33fe54b6c.png)

**・設定の確認**
各設定に間違いがないか確認後、ユーザーの作成を選択して下さい。
今回は、タグに関して触れていないので、必要な際は各自タグの設定も行って下さい。

![スクリーンショット 2022-03-08 10.47.57（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/fda8cf66-e352-a845-b676-5ff9815b6f1d.png)

**・ユーザーの詳細を確認後、.csvファイルをダウンロードする**
ユーザーの作成に成功すると、下のような画面が表示されます。
<font color="Red">.csvファイルは重要なので、ダウンロードして認証情報を保管して下さい。</font>

![スクリーンショット 2022-03-08 10.48.32（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/3e20ed4a-11d4-5667-bb9c-08c82b47602e.png)

**・コンソールへの管理アクセスを許可する**
デフォルトでは、ユーザーはコンソールへの管理アクセスを許可されていません。
従って、ユーザー詳細画面の認証情報でコンソールのパスワードを有効化する必要があります。


![スクリーンショット 2022-03-08 11.23.57（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/019fd0da-92cc-eda8-3e0f-88a6a8f19939.png)

コンソールのパスワードのリンク(管理)を選択すると下のような画面が表示されるので、コンソールへのアクセスを有効化し、自動生成のパスワードを設定して下さい。

![スクリーンショット 2022-03-08 11.28.39（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/0c8fa05e-7153-73e2-c153-98b11c05983a.png)

適用後、作成されたパスワードを確認できるので、確認して下さい。
<font color="Red">.csvファイルは重要なので、ダウンロードして認証情報を保管して下さい。</font>

![スクリーンショット 2022-03-08 11.28.49（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/41f8bd83-2b90-1700-c8d6-93d490df4d84.png)

### ・3.バケットポリシーの編集
今回の記事で作成したバケットのポリシーを編集します。

**・S3用ユーザーのARNとS3バケットのARNをコピーする**:

まず初めに、**S3用ユーザーのARN**をコピーしていつでも使用できるように保管して下さい。

![スクリーンショット 2022-03-08 11.47.19（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/8cd0d663-6529-b4ed-ab7f-7bfbdd60f4d1.png)

次に、先ほど作成したS3バケットを選択して、プロパティ欄から**バケットのARN**をコピーして保管して下さい。

![スクリーンショット 2022-03-08 11.52.17（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/7c53efea-5abc-be7d-d4eb-3bd8988107d1.png)

**・バケットポリシーを編集する**

アクセス許可タグを選択後、バケットポリシーの編集ボタンをクリックしてポリシージェネレーターを開きます。

![スクリーンショット 2022-03-08 16.43.49（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/5f8692a3-e26f-5be0-6bd8-d4b8ed875169.png)


下のようなポリシージェネレーター画面でバケットポリシーに関する必要事項を入力してバケットポリシーを編集します。

``Select Type of Policy``: S3 Bucket Policyを選択します。

``Effect``: Allowを選択します。

``Principal``: S3用のIAMユーザーのARNを入力します。

``AWS Service``: **Amazon S3**が選ばれていることを確認して下さい。

``Actions``: **All Actions('*')** を選択します。

``Amazon Resource Name(ARN)``: コピーしたバケットのARNを入力します。


![スクリーンショット 2022-03-08 11.57.52（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/69a3f123-5416-ce5b-e3b3-7384c64dcf99.png)

必要事項を入力後、``Add Statement``をクリックすることで下記のように追加され、``Generate Policy``をクリックすることで自動的に**JSONで記述されたアクセスポイントポリシー**を生成してくれます。

![スクリーンショット 2022-03-08 11.58.36（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/e4a46f2c-e092-09ac-b2fa-bd53f64f7bf5.png)

JSONで記述されたアクセスポイントポリシーをコピーした後に、アクセス許可画面に戻りバケットポリシーの編集画面を開き、デフォルトのバケットポリシーを削除して、先ほど自動生成したアクセスポイントポリシーで上書き保存して下さい。

![スクリーンショット 2022-03-08 17.01.38（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/8aca9ec8-11ca-661c-0233-875aafb87f35.png)

以上で、バケットポリシーの設定は完了です。

``~バケットポリシーの補足~``
**・Version**:
``"Version":"2012-10-17"``は、ポリシー言語の現行バージョンであり、常にVersionを2012-10-17に設定する必要があります。
このようにしない場合、このバージョンで導入されたポリシー変数などの機能は使用できません。
<br>
**・Statement**:
Statement要素は、**ポリシーの主要要素**であり必須です。
Statement要素には、単一のステートメントまたは個々または複数のステートメントの配列を含めることができます。
個々のステートメントブロックは中括弧``{ }``で囲み、複数のステートメントの場合は角括弧``[ ]``で囲み配列にする必要があります。
<br>
**・Sid**:
Sid (ステートメント ID) は、ポリシードキュメントに与える任意の識別子です。
Sid値は、ステートメント配列内の各ステートメントに割り当てることができます。
<br>
**・Resource** : 
バケット、オブジェクト、アクセスポイント、ジョブは、アクセスを許可または拒否できる Amazon S3 のリソースです。ポリシーでは、Amazon リソースネーム (ARN) を使用して、リソースを識別します。
詳細は、[リンク先](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/s3-arn-format.html)をご確認ください。
<br>
**・Action** :
Amazon S3 では、各リソースに対して一連のオペレーションがサポートされています。許可(または拒否)するリソースのオペレーションは、アクションキーワードを使用して識別します。
<br>
**・Effect**:
Effect要素は必須であり、ステートメントの結果を許可または明示的な拒否のどちらにするかを指定します。
Effectの有効値は、``Allow``と``Deny``です。
※デフォルト設定では、リソースへのアクセスは拒否されます。
<br>
**・Principal**:
ステートメントのアクションやリソースへのアクセスを許可するアカウントまたはユーザーを指定します。
指定する際は、ユーザーのARNを利用します。


## AWS S3へ画像をアップロードする設定(Laravel)
### ・1.Laravel側でleague/flysystem-aws-s3-v3のインストールを行う

Laravelで``AWS S3``を操作する際は、[thephpleague/flysystem-aws-s3-v3](https://github.com/thephpleague/flysystem-aws-s3-v3)ライブラリを利用します。

今回使用するLaravelのバージョンは6.xなので、下記の[リンク先](https://readouble.com/laravel/6.x/ja/filesystem.html#driver-prerequisites)でバージョン指定等がないか確認して下さい。
ちなみに、Laravel6.xでは``Amazon S3: league/flysystem-aws-s3-v3 ~1.0``といったようにバージョン指定されています。

```
[実行ユーザー名@ip- 特定のディレクトリ ]$ composer require league/flysystem-aws-s3-v3:^1.0
```
### ・2.``/config/filesystems.php``を編集する
/config/filesystems.phpを下記のように編集してください。

```/config/filesystems.php
'default' => env('FILESYSTEM_DRIVER', 'local'),

  // 追記
  'cloud' => env('FILESYSTEM_CLOUD', 's3'),

  // 略

  'disks' => [
    'local' => [
      // 略
    ],
    'public' => [
      // 略
    ],

    // 以下を追記
    's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
    ],
```
### ・3.環境変数の設定
.envファイルを下記のように編集してください。

```.env
AWS_ACCESS_KEY_ID='AWSで作成したS3用ユーザーのAccess key ID'
AWS_SECRET_ACCESS_KEY='AWSで作成したS3用ユーザーのSecret access key'
AWS_DEFAULT_REGION=ap-northeast-1(東京リージョンの場合)
AWS_BUCKET='作成したバケット名'
```

### ・4.アップロードするメソッドの編集
該当するControllerのメソッドで、下記のように編集してください。
``use Illuminate\Support\Facades\Storage;``を利用して、画像のアップロードやフルパスの取得を行います。

```Controller.php
public function()
{
      // バケットの`example`フォルダへアップロードする
      $path = Storage::disk('s3')->putFile('example', $image, 'public');
      // アップロードした画像のフルパスを取得
      $user->image = Storage::disk('s3')->url($path);
}
```
以上で画像のアップロードが可能となります。

## AWS S3で保存している画像を表示する設定(Laravel)
下記のように記述することで、AWS S3で保存している画像を表示できます。

```.php
# 略
<!-- 画像を表示 -->
   <img src="{{ $user->image }}">
```
以上で、LaravelアプリケーションでS3に画像を保存、そして表示できるようになります。

## 参考文献
[Amazon S3](https://aws.amazon.com/jp/s3/)
[S3 バケットでのバージョニングの使用](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/Versioning.html)
[Amazon S3 バケット向けのサーバー側のデフォルトの暗号化動作の設定](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/bucket-encryption.html)
[S3 オブジェクトロックの使用](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/object-lock.html)
[Amazon S3 コンソールを使用したバケットポリシーの追加](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/add-bucket-policy.html)
[Laravel6.x ファイルストレージ](https://readouble.com/laravel/6.x/ja/filesystem.html#driver-prerequisites)
[Amazon S3 のポリシーとアクセス許可](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/access-policy-language-overview.html)
[IAM JSON ポリシー要素のリファレンス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_elements.html)
[Amazon S3 バケットのアクセス許可](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/s3-bucket-policy.html)