S3 + Laravel + herokuで画像アップロードまとめ【忙しい人のため用】


LaravelとherokuでのS3を用いた画像アップロード記事はいくつか転がっているので、こちらの記事では要点だけを抑え、なるべく短時間でデプロイできるよう見やすく仕上げたつもりです。
そのため、S3に対する理解がとぼしくなる恐れがございます。(S3については少し調べてくるといいかもです。)
エラー解決も含め、みなさんの何かの参考になれば幸いです。

【設定環境】
M1 mac OS Big Sur 11.2.2
Composer 2.0.13
Laravel 5.8
PHP 7.2

画像アップロード&表示までの流れ(目次)

① S3の設定

② AWS SDK for PHP のインストール

③ ファイル操作用パッケージのインストール

④ Herokuの環境変数を設定

⑤ コントローラー(保存),ビュー(送信と表示)

⑥ S3のアクセス権限の許可

① S3の設定

AWSのアカウント作成

→ AWSアカウント作成の流れ

登録時に下記のものが発行されます。
● Access key ID
● Secret access key

あとで使うのでメモっとくといいです!

既にアカウントをお持ちの方はこちらで各keyを確認できます。
→ AWSアカウントID確認

バケットの作成

こちらを参考にバケットを作成できます^^
→ S3バケットの作成手順 (Qiita)

要点としては
・ 任意のバケット名を決める
・ リージョン(地域)を ap-northeast-1 に変更
・ オプションなどはスルーでOK
バケットの「アクセス権限」を許可  ←注意

【記事の注意点】
注意と書きましたが、「6. バケットの作成」のところで「パブリックアクセスを全てブロック」にしていますが、このままだと取得、アップロード時にサーバー上でエラーが起きてしまいます。

「オン」ではなく

「オフ」に変更(上の2つだけ「オフ」でも可)

すると「アクセス許可の概要」が
「非公開」

から
「公開」

に変更されているかと思います!

これでS3へのアクセスが可能となりますが、もちろんセキュリティはガバガバです、笑
使用用途、状況や使い方にはくれぐれもご注意くださいまし。
(参考:https://qiita.com/naoki-weblog/items/0ab1f017b2edc88edcba)

あと、記事中最後のフォルダ作成は作っても作らなくても後からできる(裸のままファイルを置ける)ので、作らないないのであれば「1 ~ 9」の手順でS3の設定は完了です!!

② AWS SDK for PHPをインストール

composer で依存関係として AWS SDK for PHP を追加します。

$ composer install aws/aws-sdk-php
//うまくいかない人は
$ composer require aws/aws-sdk-php
//それでもうまくいかない人は
$ composer require aws/aws-sdk-php:0.*

それでもなぜかうまくいかない人
→ (参考:https://weekend-v.work/blog/archives/269)

③ ファイル操作用パッケージのインストール

ファイル操作用パッケージ flysystem-aws-s3-v3 を追加します。

$ composer require league/flysystem-aws-s3-v3
//Laravel8.xの人は
$ composer require league/flysystem-aws-s3-v3:^1.0
//うまくいかない人は
$ composer require league/flysystem-aws-s3-v3:1.*

それでもやっぱりうまくいかない人
→ (参考:https://weekend-v.work/blog/archives/269)

④ Heroku上の環境変数を設定

ここまでうまくいくと下記のようにapp>config>filesystems.phpがあるかと思います。

filesystems.php
   'cloud' => env('FILESYSTEM_CLOUD', 's3'),
               --  --
   'disks' => [
               --  --
         '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'),
        ],
    ],
];

ここと繋がるるように.envファイルに書き加えましょう。

heroku config:set AWS_ACCESS_KEY_ID={AWSで作成したAccess key ID}
heroku config:set AWS_SECRET_ACCESS_KEY={AWSで作成したSecret access key}
heroku config:set AWS_DEFAULT_REGION={選択したリージョン名}
heroku config:set AWS_BUCKET={作成したバケット名}
heroku config:set AWS_URL=https://s3-{選択したリージョン名}.amazonaws.com/作成したバケット名/

AWS_URL=がなかったり間違っているとHerokuとS3(AWS)が繋がらないのでご注意を。

これでおそらく


ローカルとS3
HerokuとS3

が繋がっている状態です!

⑤ コントローラー(保存),ビュー(送信と表示)

View (送信:POST)

create.blade.php
<form method="POST" action="{{ route('tweets.store') }}" enctype="multipart/form-data">
    @csrf

    <div class="col-ms-2 mt-3">
        <input type="file" name="image" autocomplete="image" rows="4" value="画像を選択">
    </div>

    <div class="form-group row mb-0">
        <div class="col-md-12 text-right">
            <button type="submit" class="btn btn-primary">
                ツイートする
            </button>
        </div>
    </div>

</form>

画像やファイルをinputタグで送るとき
formタグに enctype="multipart/form-data" を入れてあげないとnull扱いになってしまうのでご注意を。

コントローラー (保存:Store)

TweetsController.php
use Illuminate\Support\Facades\Storage; //追記(いらないかも?)

    public function store(Request $request)
    {
        $tweet = new Tweet();

        if($request->file('image')->isValid()) {
            $file = $params['image'];
            //バケットにフォルダを作ってないとき(裸で保存)
            $path = Storage::disk('s3')->put('/',$file, 'public');
            //バケットに「test」フォルダを作っているとき
            $path = Storage::disk('s3')->put('/test',$file, 'public');
            $tweet->image = $path;
        }

        $tweet->save();
        return redirect('/tweets');

もっと綺麗に記述できるのですがすみません。。。
他にもputputFileputFileAsなども使える?みたいなので、上手くいかない人は参考までに。
ちなみに第3引数の'public'は外部からのアクセスの可否。publicにすることで許可され、画像データの受け渡しができるようになるそうです。

View (表示:GET)

index.blade.php
   <img src="{{ Storage::disk('s3')->url($tweet->image) }}">

⑥ エラー解決

Your requirements could not be resolved to an installable set of packages.
Installation failed, reverting ./composer.json to its original content.
The GetObject operation requires non-empty parameter: Bucket 
Found 1 error while validating the input provided for the GetObject operation: [Key] expected string length to be >= 1, but found string length of 0
Found 1 error while validating the input provided for the GetObject operation: [Bucket] is missing and is a required parameter
Found 1 error while validating the input provided for the PutObject operation:
[Bucket] is missing and is a required parameter

以上のようなエラーが起きた場合は状況に応じて以下を参考に実行してみてください^^
【候補一覧】
・ S3側にアップしたファイルの「アクセス権限」は公開になっているか
・ config>filesystem.phpが作成されているか
・ use Illuminate\Support\Facades\Storage;のつけ忘れ
・ Heroku側のDB削除 (ホームページ->Resources->Heroku Postgres->Setting->Reset Database)
・ 画像フォルダ名は間違っていないか
・ キャッシュのクリア (参考:https://qiita.com/Ping/items/10ada8d069e13d729701)
・ composer.lockを消してみる (参考:https://weekend-v.work/blog/archives/269)
・ S3(AWS)のバケット名、URLが間違っていないか (.envファイルと比較して確認)

$ heroku config

・ キーの作り直し

$ php artisan key:generate

・ composerのアップデートorインストール

$ composer update
//または
$ composer install

 参考にした記事

•  Laravelでファイルを扱うとき
•  「Herokuにデプロイしたページの画像が表示されない!?」を解決する AWS S3 超カンタン利用法
•  【Laravel】 The PutObject operation requires non-empty parameter Bucket エラーの解消法
•  バケットの作成手順
•  Laravel で composer require をすると “Installation failed, reverting ./composer.json to its original content.” が出る