【Laravel】画像をアップロードして、表示させる方法

やりたいこと formから、画像をアップロードしてそれを表示させる。

書いてる人 書いている時点でプログラミング歴9カ月突入の初心者

知ってたらこの記事理解しやすいモノ HTMLのformの知識 パスとシンボリックリンク

前提  Laravelプロジェクトを立ちあげられること。作ったことない人はこっからどうぞ。LaravelのMVCモデルがわかってること。(ルーティングとかコントローラーとかの説明は省略してるのでこれがわからないと、見ても意味ない。)

環境: macOS Catalina
この記事ではlaradockを使用してプロジェクトを作成しています。所々自分の環境に合わない方はかいつまんで見てください。

参考サイトhttps://reffect.co.jp/laravel/how_to_upload_file_in_laravel#i-13 

Laravelで画像ファイルなどをフォームから送って、それを表示させたい。
流れとしては画像をアップロードしてLaravelに保存→そのパスをDBに保存→保存したパスの情報を元に画像を表示という感じだ。
備忘録としてまとめます。


実際に作って確かめたい人は適当なLaravelプロジェクトを作成しておこう。1個くらいすぐに練習できる環境を作っておくと楽だ。コントローラーやビューファイルは記事を読んでいきながら適宜作ってみてください。

目次

Bladeにformを用意する

まずは、画像をアップロードできるように、Bladeにformを用意する。
画像ファイルはDBに直で保存はしない。様々なデメリットの方が多いらしいので、DBには、画像ファイルのパスだけ保存しておくみたい。画像自体はLaravelプロジェクトの中に入っていく。(後述)


パスとは目的のファイルまでの道のりみたいなことで、パスには絶対パス相対パスがある。絶対パスは、道のりを最初から最後まで書く、相対パスは今いる場所からの道のりを書いていく。詳しくはググろう。調べればたくさん出てくる。

参考:https://webliker.info/78726/

Bladeファイルを適当に用意して、以下のように記述していこう。
formタグにenctype="multipart/form-data"属性を用意する。ファイルアップロードをするためにはこれを書く必要がある。

<form action="/upload" enctype="multipart/form-data" method="post">
    @csrf
    <input type="file" name="imgpath">
    <input type="submit" value="アップロードする">
</form>

web.phpでルーティングをして、上で作ったBladeファイルにアクセスしよう。ここまでよくわからないという人は、まずはLaravelのMVCモデルを理解しよう。そのうち記事でも作るかもしれないが、他にもたくさん優良の記事がネットに落ちているので調べてみよう。アクセスしたら以下のような表示になるはず。

これでとりあえずは、画像をアップするフォームの完成だ。

コントローラーを作成して、送ったデータを確認

次に、送った画像ファイルをコントローラー側のstoreメソッドでとってくる。コントローラーを作成して、コードを記述したら、ファイルを送ってみよう。ddで確認するとUploadFileインスタンスに情報が保持されていることがわかる。ざっくり言うと、画像データを送ってそれがちゃんと取れてるか確認しただけだ。

//作成したコントローラーに記述
//$request->inputのname属性でつけた名前でデータがとれる。(この場合imgpath)

public function store(Request $request){
     $img=$request->imgpath;  //formで設置したname名
   //$img=$request->file(imgpath); fileメソッドを使うとファイル?だけが取れるそうだが変わらなかったので使っても使わなくてもどっちでも良いと思う。
     
     dd($img);
}
今回は好きなラーメン二郎の写真をアップロード
送ったデータをddで確認。ちゃんとデータが取れてる。

そして、UploadFileインスタンスはstoreメソッドとsotreAsメソッドを持っており、これを使ってLaravel側に保存する。
両者の違いは保存される画像ファイルの名前がどうなるかだ。

storeメソッドを使ってみよう

とりあえず、storeメソッドを追記して、画像を送ってみよう。store(' ')の「' '」を忘れずに!

public function store(Request $request){
   $img=$reauest->imgpath->store('');  //storeメソッドを追加
}

送っただろうか?
では、アップロードした画像ファイルはLaravelプロジェクトのどこへ保存されるのか確認していこう。
Laravelでは、Laravelプロジェクトのstorage/appフォルダ直下に入るようになっている。

storeメソッドなので、ファイル名はなんかすごい感じになってる。

store(' ') の( )に('好きな名前')をつけると、storage/app/好きな名前 直下にはいる。名前をつけフォルダが作成されるわけだ。

※日本語版のリファレンスでは、ファイルパス名が作られると書いてある。storage/app/好きな名前 のパス名が作成される。(フォルダ名が作られるって覚えた方がわかりやすい)

公式より
フォルダ名を指定した。
ちゃんとramenフォルダが作成されてその中にファイルが保存されている。

storeAsメソッドを使ってみよう


storeメソッドを使えば、Laravelプロジェクトの中にファイルを保存することができた。
ただ、storeメソッドだとファイル名が任意に決められず、勝手に名前が付けられてしまう。任意の名前をつけたい場合にはstoreAsメソッドを使おう。

名前の付け方はなんでもいいが、ファイルについていた元々の名前をそのまま付けたい場合はgetClientOriginNameメソッドを使いオリジナルの名前を取得する。

public function store(Request $request){
     $titlename=$request->imgpath->getClientOriginalName()
     //getClientOriginalNameでオリジナルの名前が取れる。
}
Laravel日本語版の公式から
ddで確認。getClientOriginalName()では、拡張子まで取得される。


そして、storeAsの引数に突っ込む。

public function store(Request $request){
     $filename=$request->imgpath->getClientOriginalName()
     //getClientOriginalNameでオリジナルの名前が取れる。
   
   $img=$request->imgpath->storeAs('',$filename); 
   //storeAsメソッドを追加して引数に上で取得したオリジナル名を入れる。
}
今度は名前がしっかり入った状態になった。

後述するが、Laravelで画像を表示させるためにシンボリックリンクというのを貼る必要がある。そして、その為にはstorage/app/public/直下に画像を入れて置かなければならない。storeメソッドとsoterAsメソッドの引数にpublicを指定しstore('public') storeAs('public') のような使い方をすれば保存できるのだが、これだと返ってくるパス名がpublic/画像.jpg になってしまう。個人的にはこれが気に入らないので、下の発展の方法を使用しています。

storeメソッドとstoreAsメソッドの使い方はこんな感じだ。なんとなくわかっただろうか。以下少し発展要素となるので余裕のある人は軽く目を通しておいたらいいかも。

発展
実は、storeメソッドは2つの引数、storeAsメソッドは3つの引数を持つ。
storeメソッドは1.フォルダ(詳しくはパス名だが)2.ディスク名
storeAsメソッドは1.フォルダ(詳しくはパス名だが)2.ファイルネーム 3.ディスク名となっている。
この3番目のディスク名は正直初心者はよくわからなくてもよいと思う。ただ、このディスク名にpublic を指定しておけば、1.フォルダ名に名前をつけなくてもstorage/app/public/ に画像が保存される。
じゃあ「最初からやれよ!」と言われるかもしれないが、書きながら勉強していたので勘弁してください。。何事も基礎の基礎からわかった方がいいしね。。。
pathは空白にしておけばよいので、store(' ', public) storeAs(' ',$filename,public) だけで勝手にstorage/app/public 下に画像が保存される。しかも、DBにパスを保存するときはこっちの方が都合がよかったりもする。もっと詳しく知りたい人は公式ドキュメント飛んでってください。config/filesystem.php でディスクの管理ができる。

画像ファイルのパス名をDBに保存する

さて、これでアップした画像がLaravelのstorage/app/public/ 直下に保存された。(storage/app/ 直下に入れている人はここでVScodeなどを使ってファイルを移動しておいて欲しい。後々Blade側で表示する時にこっちの方が都合が良いので。)

storeメソッドとstoreAsメソッドの返り値は保存したファイルのパス名なので、あとは、そのパス名をDBに保存してあげる。

下のコードでDBへ画像が保存される。
※なお、都合上storeAsメソッドを使用し、第一引数は空、第三引数はpublicを指定してある。こうすると、storage/app/public/直下に画像が保存される。気になる人は↑の発展を見ておいてほしい。

class Practicecontroller extends controller
{
    public function store(Request $request)
    {
        //画像のオリジナルネームを取得
        $filename = $request->imgpath->getClientOriginalName();

        //画像を保存して、そのパスを$imgに保存 第三引数に'public'を指定
        $img = $request->imgpath->storeAs('',$filename,'public');

      //ユーザークラスのインスタンス化
        $user = new User();

        //imgpathカラムに画像パスを挿入
        $user->create(['imgpath'=> $img]);
        return view('practice');
    }
}
'二郎.jpg'というパスが保存された。

storeAsメソッドの返り値はあくまでパスだ。画像自体はstorage/app/public/ 下に保存されており、DBに保存されてるわけではない。

今回Userモデルにimgpathを用意して、createメソッドで保存した。DBへの保存の方法はネットにたくさんやり方が落ちてるので確認してほしい。

シンボリックリンクを作成する

さあ、これでデータは保存された。後はBlarde側で表示させるだけだ。ここで、シンボリックリンクが登場する。
シンボリックリンクに関してはわからない方はを↓を参照!

Laravelの仕様上、ローカルではLaravelproject/publicになっているが、サーバーにアップした段階でpublicがルートフォルダ(一番上のフォルダ)になる。publicの中に画像を保存して置かないと、画像が表示されないのだ。画像はstorage/app/ 以下に保存されるのにどうやって表示するのだろうか?

画像が反映されるのはpublicフォルダ。保存されるのは、storage/app以下
ここらへんややこしくて嫌になる

そこで、php artisanコマンドを使ってシンボリックリンクを作成する。以下のコマンドをLaravelprojectフォルダで打つ。(artisanファイルがあるところ)
ただし、このシンボリックリンクのコマンドを打つ場所はコンテナの中プロジェクト直下だ!docker-compose exec workspace bash でworkspaceに入ったらLaravelprojectまで移動して以下のコマンドを打とう。(DockerとかLaradockでLaravel使っている人は!)

自分はこのコマンドを、コンテナの外で行ったことにより、画像が表示されず1日彷徨ってしまった、、

//適切な場所でコマンドを入力しよう。laradock使っている人はコンテナの中だ
php artisan storage:link

コマンドを打つとちょっと長いが、シンボリックリンクが作成される。シンボリックリンクが作成されたらpublicフォルダを覗いてみよう。

成功例:コンテナの中でシンボリックリンクが作成できた。
失敗例:コンテナの外でシンボリックリンクを作成してしまった。
成功するとpublicの直下にstorageが表示された。クリックしても開けないけどまあいっか。

publicフォルダでls -lコマンドを打つことでも確認できる。

「storageフォルダの中は、→の先に表示されているのフォルダとリンクしてます」という意味だ。

Blade側で画像を表示する

上記の過程で、Laravelproject/public/storageLaravelproject/storage/app/publicがリンクされた。
storageという単語とpublicという単語がたくさん出てくるので混乱するかもしれないが、自分がどのフォルダとどのフォルダをリンクさせているかを意識して行うと理解しやすいかもしれない。


最後に、Blade側で画像を表示させれば完了だ。

asset()関数を使い以下のように表示する。最初は、DBからパスを読み込むのではなく直接ファイルパスを指定して表示させてみる。
※asset()関数は、URLを生成してくれる関数だ。

//二郎.jpgを直接記述。
<img src=" {{ asset('storage/二郎.jpg')}}">
表示はできたが、画像サイズがデカすぎる、、、

一旦画像のサイズは置いといて、次にDBに保存してあるパスをとってきてそれを反映させてみる。該当のファイルパスをとってくるやり方は色々あると思うが下のは一例だ。
また、この下のコードでなぜDBから画像パスが取得できるのかわからない人は「データをDBに保存→DBから欲しいデータを引き出す→表示させる」といった練習をしてみると良いかも。

class Practicecontroller extends controller
{
    public function store(Request $request)
    {
     //画像ファイルオリジナルの名前を取得。
        $filename = $request->imgpath->getClientOriginalName();
     //storeAs関数でstore/app/publicに画像を保存し、そのパスを$imgに入れる。
        $img = $request->imgpath->storeAs('',$filename,'public');

        //ユーザークラスのインスタンス化
        $user = new User();
        //Userテーブルのimgpathカラムに画像パスを挿入 $dataには画像パスを挿入したUserのレコードが取得されている。
        $data = $user->create(['imgpath'=> $img]);
     //dataをcompactを使ってpractice.bladeに送る。
        return view('practice',compact('data'));
    }
}
bladeに送られた、$data(ユーザーのレコード)からimgpathを指定してパスを取り出す。
同じように画像が大きすぎるが表示できた。

画像のサイズは一旦置いといて、これで画像を表示させることができた。DBからわざわざパスを持ってきて表示させる必要はないかと思うかもしれないが、実際に何かしらのアプリを作る時はDBに保存してその保存されたデータを引き出して表示させることが必要になってくる。特にLaravelで画像投稿付きのアプリを作ろうと思っている人は参考にしてもらえればと思います。

参考:画像のサイズを変更して保存する。

画像サイズを変更する場合はIntervention Imageという外部のライブラリを使った。DBにはファイル名を保存して、実際のLaravelの中にはリサイズされた画像を保存してある。参考にコードは以下のようなものだ。

InterventionImageというライブラリを使ってサイズを変更して保存した。
なんか表示は崩れてるけど、リサイズはされた!

外部のライブラリの使い方や、画像サイズ変更はまた別の記事でしっかりまとめていきます。

まとめ

ここまでを改めて整理する。

  1. bladeでformにenctype="multipart/form-data"input type file name="好きな名前"を用意
  2. コントローラー側でそのファイルを受け取り、storeかstoreAsメソッドでLaravelproject側に保存
  3. storeかstoreAsメソッドの返り値(パス)をDBのカラムに保存する。
  4. DBから、そのパスを引き出してblade側で表示する。

流れとしては非常に単純だが、これを初心者が実際に行うのは大変だ。自分もものすごく時間がかかった、、。一応なんとか自分なりにまとめることはできたと思う。
コードはもっと綺麗に書けるのかもしれないが、そこらへんは多めに見てください、、!

画像ファイルを送るだけでこんなにややこしいとは。頑張るしかない、、!!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次