LoginSignup
19
22

More than 3 years have passed since last update.

Laravelで画像ファイルをアップロードする

Posted at

Laravelでユーザーのアバター画像をアップロードする想定で実装してみました。

テーブルにカラムを追加する

usersテーブルに画像のパスを保存するカラムを追加し、migrateを実行します。

add_image_path_to_users_table.php

class AddImagePathToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('image_path')->after('email_verified_at');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('image_path');
        });
    }
}

画像をアップロードするフォームを作成する。

ここで注意点があります。
① Laravelでフォームを作成する際、CSRF対策がなされていないと、エラーになってしまうので、素のHTMLタグでフォームを作成する際は{{ csrf_field }}を追記しましょう。

{{ csrf_field }}で出力される内容は以下のようになります。type属性はhidden。フォームデータを送信する時に、ハッシュ値がリクエストされます。

<input type="hidden" name="_token" value="tyKtcrH2KscjxVAsxb3KHXG5ppZ8te6PA6SJY8df">

※CSRFとはクロスサイトリクエストフォージェリの略で、Webアプリケーションの脆弱性をついた攻撃手法です。信頼できるユーザーになりすまし、ユーザーの不利益になるような処理を行うことができます。Laravelではこの対策として、各セッションごとにトークンを自動的に生成しています。

webミドルウェアグループに含まれている、VerifyCsrfToken ミドルウェアが、リクエスト中のトークンとセッションに保存されているトークンが一致するか、確認しています。
https://readouble.com/laravel/5.7/ja/csrf.html

②formタグに enctype='multipart/form-data' を忘れず、記述しましょう。これは、複合データ型であることを示し、1回のHTTP通信で、複数の種類のデータ形式を扱う事でできるようになります。(今回の場合は、jpeg、png等)

edit.blade.php
@section('content')
    <form route="/users/{$user->id}/edit" method="post" enctype='multipart/form-data'>
        {{ csrf_field() }}

        @isset ($filename)
            <div>
                <img src="{{ asset('storage/avatar/' . $user->image_path) }}">
            </div>
        @endisset

        <div>
            <input type="hidden" name="id" value="{{$user->id}}">
        </div>
        <div>
            <input type="text" name="name" value="{{$user->name}}">
        </div>
        <div>
            <input type="text" name="email" value="{{$user->email}}">
        </div>
        <div>
            <input type="file" name="image">
        </div>
        <input type="submit" value="更新する">
    </form>
@endsection

Routeを追加する

web.php
Route::get('/users/{user}/edit', 'UserController@edit')->name('users.edit');
Route::post('/users/{user}/edit', 'UserController@update');

Controllerを定義

fileメソッドにはinputタグのname属性を、storeメソッドには、保存したいパス(ディレクトリ)を指定します。
(任意のファイル名を指定したい場合はstoreAsメソッドを使用してく

ここではユーザーのavatarをアップロードする事を想定して、public/avatarを指定します。ファイルが保存される場所は、storage/appディレクトリになるので、この場合、storage/app/public/avatarディレクトリに画像が保存されます。

次に、このままでは、カラムにパスの情報ごと保存されてしまうので、basename()を使用して、画像名のみ保存するようにしています。

viewで表示する際にはasset()を使用して <img src="{{ asset('storage/avatar/' . $user->image_path) }}"> と指定します。

UserController.php

public function edit(Request $request, User $user)
{
    return view('users.edit', compact('user'));
}

public function update(Request $request, User $user)
{
    $user->name = $request->name;
    $user->email = $request->email;
    $path = $request->file('image')->store('public/avatar');
    $user->image_path = basename($path);
    $user->save();

    return view('users.edit', compact('user'))->with('filename', basename($path));
}

シンボリックリンクを張る

最後にシンボリックリンクを張りましょう。

アップロードしただけでは、ブラウザからアップロードした画像を表示する事は出来ません。

ブラウザからサーバー上のファイルにアクセスするには、publicディレクトリの下に保存する必要があります。しかし、アップロードしたファイルが保存される場所は、storage/appディレクトリになるので、public/storageからstorage/app/publicへリンクを持たせる必要があります。Laravelではそのコマンドが用意されています。以下、コマンドを実行しましょう。

$ php artisan storage:link

リンクが貼られていれば、public/storageにリンクされている事を示すiconが表示されます。
スクリーンショット 2019-08-12 13.12.50.png

以上、自分用のメモの意味合いが強いですが、これで画像のアップロード、表示が実現できます。
もっと良い方法があれば、コメント頂けると嬉しいです。

19
22
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
19
22