LoginSignup
7
0

More than 1 year has passed since last update.

[Laravel]AWS S3に画像をアップロードする際に発生したエラー

Posted at

ポートフォリオに画像投稿機能を実装しました。
画像ファイルをAWSのS3に保存する際に、いくつか躓いた点があったので、それをアウトプットしようと思います。

バージョン

PHP 7.4.2
Laravel 6.20.26
Docker 20.10.6
docker-compose 1.29.1
OS Windows

編集前のコード

画像ファイルをS3に保存する前はpublicフォルダに保存していました。
「S3に保存する前に、とりあえず画像機能を実装したい」という方は、下記のコードをご参照ください。
画像投稿に該当する箇所のみ記載しています。
また、【Laravel 7.x】Laravelで画像投稿機能を実装を参考にしつつ、コーディングしています。

web.php
//略
Route::get('/', 'ArticleController@index')->name('articles.index');
Route::resource('/articles', 'ArticleController')->except(['index', 'show'])->middleware('auth');
Route::resource('/articles', 'ArticleController')->only(['show']);
//略
Article.php
//略
class Article extends Model
{
    protected $fillable = [
        'title',
        'body',
    ];
//略
ArticleRequest.php
    //略
    public function rules()
    {
        return [
            'image' => 'mimes:jpeg,jpg,png,gif|max:10240',
            'title' => 'required|max:50',
            'body' => 'required|max:500',
            'tags' => 'json|regex:/^(?!.*\s).+$/u|regex:/^(?!.*\/).*$/u',
        ];
    }
    //略
ArticleController.php
    //略
    public function store(ArticleRequest $request, Article $article)
    {
        $article->fill($request->all());
        if ($request->file('image')){
            $filename = $request->file('image')->store('public'); // publicフォルダに保存
            $article->image = str_replace('public/', '', $filename); // 保存するファイル名からpublicを除外
        }
        $article->user_id = $request->user()->id;
        $article->save();

        //略

        return redirect()->route('articles.index');
    }
    //略
〇〇_create_articles_table.php
    //略
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');
            $table->text('body');
            $table->bigInteger('user_id')->unsigned();
            $table->timestamps();
        });
    }
    //略
〇〇_add_image_to_articles_table.php(imageカラムを後から追加しています)
//略
class AddImageToArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('articles', function (Blueprint $table) {
            $table->string('image')->nullable()->after('id');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('articles', function (Blueprint $table) {
            $table->dropColumn('image');
        });
    }
}
index.blade.php
                //略
                <img src="{{ asset('/storage/'.$article->image)}}" alt="">
                //略
create.blade.php
//略
    <form method="POST" action="{{ route('articles.store') }}" enctype="multipart/form-data">
        @csrf
        <div class="container">
            <div class="row">
                <div class="offset-md-2 col-md-8">
                    //略
                    <div class="form-group">
                        <input type="file" class="from-control-file" id="image" name="image">
                    </div>
                </div>
            </div>
        </div>
        <button type="submit" class="btn aqua-gradient btn-block"><i class="fas fa-pen mr-1"></i>投稿する</button>
    </form>
//略

事前準備

S3に保存するためには、事前にAWSの方でIAMユーザーやS3バケットの設定が必要になります。
Rails, Laravel(画像アップロード)向けAWS(IAM:ユーザ, S3:バケット)の設定の手順で進めてください。(この記事とてもわかりやすかったです!)

編集箇所

LaravelでAWS S3へ画像をアップロードするを参考にしつつ、コードを修正しました。(この記事も非常にわかりやすかったです!)

ArticleController.php
    //略
    public function store(ArticleRequest $request, Article $article)
    {
        $article->fill($request->all());
        if ($request->file('image')){

            //==========ここから削除==========
            $filename = $request->file('image')->store('public'); // publicフォルダに保存
            $article->image = str_replace('public/', '', $filename); // 保存するファイル名からpublicを除外
            //==========ここまで削除==========

            //==========ここから追記==========
            // s3アップロード開始
            $image = $request->file('image');
            // バケットの`myprefix`フォルダへアップロード
            $path = Storage::disk('s3')->putFile('myprefix', $image, 'public');
            // アップロードした画像のフルパスを取得
            $article->image_path = Storage::disk('s3')->url($path);
            //==========ここまで追記==========

        }
        $article->user_id = $request->user()->id;
        $article->save();

        //略

        return redirect()->route('articles.index');
    }
    //略
index.blade.php
                //略
                <img src="{{ asset('/storage/'.$article->image)}}" alt=""> //この行を削除
                <img src="{{ $article->image_path }}"> //この行を追記
                //略

画像ファイルの保存先をAWS S3に変更した際のエラー

Class 'App\Http\Controllers\Storage' not found

img s3 error.png
一番最初のエラーはこれでした。
Storageが見つからないと言われています。
ArticleController.phpにuse Illuminate\Support\Facades\Storage;を追記することで、storage facadeをインポートし、このエラーは解決しました。
use Storage;でも試してみましたが、問題なく動作しました。

Class 'League\Flysystem\AwsS3v3\AwsS3Adapter' not found

aws_s3_error.png
次のエラーはこれです。
単純にflysystem-aws-s3-v3のインストールを忘れていました。
ドキュメントにもちゃんと書いてありますね。(Laravel 6.x ファイルストレージ
$ composer require league/flysystem-aws-s3-v3でインストールできます。
ただし、バージョンの指定がないと2.0がインストールされてエラーになることもあるらしいので、$ composer require league/flysystem-aws-s3-v3 ~1.0にしておいた方が安心ですね。
私の場合はDockerのappコンテナの中で実行したかったので、$ docker-compose exec app composer require league/flysystem-aws-s3-v3 ~1.0でインストールしました。
flysystem install.png

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'image_path' in 'field list'

mysql error.png
最後のエラーはこれです。
「articlesテーブルにimage_pathカラムがない」と言われています。
mysqlでarticlesテーブルのカラムを確認すると、、、

mysql> show columns from articles;
+------------+-----------------+------+-----+---------+----------------+
| Field      | Type            | Null | Key | Default | Extra          |
+------------+-----------------+------+-----+---------+----------------+
| id         | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| image      | varchar(255)    | YES  |     | NULL    |                |
| title      | varchar(255)    | NO   |     | NULL    |                |
| body       | text            | NO   |     | NULL    |                |
| user_id    | bigint unsigned | NO   |     | NULL    |                |
| created_at | timestamp       | YES  |     | NULL    |                |
| updated_at | timestamp       | YES  |     | NULL    |                |
+------------+-----------------+------+-----+---------+----------------+
7 rows in set (0.01 sec)

確かにないですね。
image_pathカラムではなく、imageカラムが正解でした。
下記のように修正しました。

ArticleController.php
    //略
    public function store(ArticleRequest $request, Article $article)
    {
        $article->fill($request->all());
        if ($request->file('image')){

            // s3アップロード開始
            $image = $request->file('image');
            // バケットの`myprefix`フォルダへアップロード
            $path = Storage::disk('s3')->putFile('myprefix', $image, 'public');
            // アップロードした画像のフルパスを取得
            $article->image = Storage::disk('s3')->url($path); //この行を修正

        }
        $article->user_id = $request->user()->id;
        $article->save();

        //略

        return redirect()->route('articles.index');
    }
    //略
index.blade.php
                //略
                <img src="{{ $article->image }}">
                //略

バケットを確認

これで無事にS3へ保存することができるようになりました。
念のため実際にアップロードして、バケットへ画像がアップロードできているか確認しました。
s3 check.png
ちゃんと保存できていますね!:grin:

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0