LaravelでS3に画像アップロードする方法

AWS
Screenshot

こんにちは!

突然ですが、皆さんは画像保存する場合にどのように保存していますか?

Webサーバーに保存するというのは一般的かもしれません。

ただ、Amazon S3に画像を保存すると、Webサーバーに画像保存する必要がなくなり、容量を圧迫せずに済んだりと何かとメリットが大きいです。

Amazon S3を活用するメリットは、一般的には以下のようなものがあるとされています。

  • スケーラビリティ・・・大規模なトラフィックやデータ増加に耐えられる
  • 低コスト・・・使用した分だけ課金される従量課金制。コストを最小限に抑えられる
  • 高速なデータ転送・・・高速かつ安定したデータ転送ができる
  • セキュリティとアクセス制御・・・バケットポリシーやIAMロールを使用して、データへのアクセスを制御できる

などなど。

そこで、今回はLaravelを使ったS3への画像アップロード方法をご紹介していきます。

読者
読者

「LaravelでS3に画像保存する手順を知りたい」

このようにお考えの方には参考になるかと思います。

それでは、早速見ていきましょう!

前提

  • AWSアカウントをすでに持っていること

LaravelからS3へ画像アップロードする方法

バケットを作成する

まずは、S3にバケットを作成していきます。

AWSの「Amazon S3」に入り、以下の「バケットを作成」をクリック。

Screenshot

バケットの設定を行う

    • バケットタイプ・・・汎用
    • バケット名・・・任意の名前
    • オブジェクト所有者・・・ACL 無効
    • ブロックパブリックアクセス・・・「パブリックアクセスをすべてブロック」を解除
    • バケットのバージョニング・・・無効にする
    • タグ・・・なし
    • デフォルトの暗号化・・・Amazon S3 マネージドキーを使用したサーバー側の暗号化 (SSE-S3)。バケットキーは有効にする

    これらを設定し、「バケットを作成」をクリック。

    バケットポリシーの編集

      1. 対象のバケットを選択
      2. 「プロパティ」タブを開く
      3. ARNをコピーしメモしておく
      4. 「アクセス許可」タブを開く
      5. バケットポリシーの編集ボタンをクリック
      6. ポリシージェネレータを開く

      ポリシージェネレータでは、以下のように入力します。

      • Select Type of Policy・・・S3 Bucket Policy
      • Effect・・・Allow
      • Principal・・・「*」を入力
      • AWS Service・・・Amazon S3
      • Actions・・・All Actionsにチェック

      入力したら、「Add Statement」をクリックします。

      続いて「Generate Policy」をクリックします。

      Policy JSON Documentが出てくるのでコピーして閉じる。

      バケットポリシー編集画面に戻ってコピーしたPolicyをペーストし、変更を保存する。

      IAMユーザーを作成

      次に、IAM > ユーザーに入って、「ユーザーの作成」をクリックします。

      ユーザー名には適当なユーザー名を入力します。

      Screenshot

      次に、以下のように設定します。

      • 許可のオプション・・・「ポリシーを直接アタッチする」を選択
      • 許可ポリシー・・・「AmazonS3FullAccess」を選択
      Screenshot

      最後に確認画面が出てくるので、問題なければ「ユーザーの作成」をクリック。

      ユーザーが作成されるので、詳細画面に入って、「アクセスキーを作成」をクリック。

      Screenshot

      ユースケースに「コマンドラインインターフェース(CLI)」を選択します。

      Screenshot

      設定タグ値には任意の説明を入れて、「アクセスキーを作成」をクリック。

      アクセスキーとシークレットキーが作成されるので、CSVダウンロードして漏れないように大切に保管してください。

      このアクセスキーとシークレットキーは後ほど使っていきます。

      LaravelプロジェクトにS3用パッケージをインストールする

      以下のコマンドからFlysystem S3パッケージをインストールします

        composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies

        S3用の設定を追加

          「config/filesystems.php」に以下を追記します。

          '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'),
              'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
              'throw' => false,
          ],

          環境変数の設定

            AWS用の環境変数を設定します。

            AWS_ACCESS_KEY_ID=アクセスキー
            AWS_SECRET_ACCESS_KEY=シークレットキー
            AWS_DEFAULT_REGION=ap-northeast-1(東京リージョンの場合)
            AWS_BUCKET=バケット名
            AWS_USE_PATH_STYLE_ENDPOINT=false

            アクセスキーには作成しておいたIAMユーザーのアクセスキーとシークレットキーを記述します。

            S3へのアップロード処理

              以下、S3へのアップロード処理の一例です。

              public function store(Request $request)
              {
                  ...
                  // s3に保存
                  $path = Storage::disk('s3')->put('images', $request->file('image'));
                  $post->image_path = Storage::disk('s3')->url($path);
                  $post->save();
              
                  return redirect()->route('post.index');
              }

              Storageファサードのメソッドはputを使っていますが、そのほかにもputFileputFileAsメソッドなどがあります。

              putはメモリ消費が一番大きいのでここは適時検討してください。

              フォーム

                <form action="{{ route('post.store') }}" method="post" enctype="multipart/form-data">
                    @csrf
                    <div class="form-group">
                        <label for="post-image">投稿画像</label>
                        <input type="file" name="image" id="post-image">
                    </div>
                    ...
                    <button type="submit">投稿</button>
                </form>

                formタグに「enctype=”multipart/form-data”」の記述を忘れないよう注意です。

                これを忘れると動きません。

                画像表示

                  @foreach ($posts as $post)
                  	<div class="card">
                  	    <div class="card-img">
                  	        <img src="{{ $post->image_path ? $post->image_path : 'img/sample-img.jpeg' }}" alt="サンプル画像">
                  	    </div>
                  	    ...
                  	</div>
                  @endforeach

                  DBに保存しておいた画像パスを取得してsrc属性に入れています。取得できなかった場合は、事前に用意しておいたサンプル画像を差し込んでいます。

                  テストコード

                    use Illuminate\\Support\\Facades\\Storage; // 追加
                    use Illuminate\\Http\\UploadedFile; // 追加
                    
                    public function test_store(): void
                    {
                        Storage::fake('s3');
                        $file = UploadedFile::fake()->image('test.jpg');
                        // login_userが認証を通過しているとする
                        $this->assertAuthenticatedAs($this->login_user);
                        $response = $this->actingAs($this->login_user)->post(route('post.store'), [
                            'title' => 'Test Title',
                            'body' => 'Test Body',
                            'image' => $file,
                        ]);
                        $response->assertStatus(302);
                        Storage::disk('s3')->assertExists('images/' . $file->hashName());
                        $response->assertRedirect(route('post.index'));
                     }

                    Storage::fake()メソッドとUploadedFile::fake()メソッドを使えば、簡単にテストすることができます。

                    動作確認

                      最後に、動作確認。

                      Screenshot

                      画像表示ができました!素晴らしい!👏

                      コメント

                      タイトルとURLをコピーしました