今回は、Laravel6を使って、画像をアップロードし、それを表示させるチュートリアルをやっていきます。
前回のチュートリアルでは、WYSIWYGエディタを使って、画像付きのブログ投稿をするチュートリアルをやりました。
→Laravel6チュートリアル:WYSIWYGエディタ(Quill)を使ってみよう!
今回は、普通のフォームを使い、投稿に1画像を添付するタイプの
簡単な画像アップロード機能をLaravelで作っていきます。
例えば、このように投稿画面に画像アップロード機能を追加し、
送信すると、LaravelのStorageフォルダに画像が保存された後、
このように、画像が投稿と共に表示されるというものを作っていきます。
この機能は、画像以外にも、動画や音声、PDFファイルなど、あらゆるファイルのアップロードに応用できるので、ぜひ使いこなせるようになっていきましょう。
Laravel6チュートリアル:画像投稿機能を付加しよう!
始める前に
このチュートリアルをやるには、データベース、モデル、投稿表示画面などがすでに作成されている必要があるため、次の2つのチュートリアルで先にそれらを作成しておく必要があります。
もし、データベースなど下準備不要で、画像アップロードする機能だけ付けたい場合は、こっちのチュートリアルでやってるよ↓
今回の画像アップロード機能の追加は
- 画像フォルダにシンボリックリンクを貼る
- ブレードに画像投稿ボタンを追加
- コントローラーに画像保存命令を書く
- ルートを設定する
- 画像が保存できてるか確認
- 画像を呼び出し、ブレードに表示する
という流れで作っていきます。
画像フォルダにシンボリックリンクを貼る
まず、Laravelで画像や動画、音声など、何かファイルをアップロードすると、
storage\appフォルダ内に保存されます。
ただ、storage\appフォルダに直接画像を保存した場合、
ブラウザからその画像が参照できません。
というのも、ブラウザから誰でも見れるモノというのは、
publicフォルダに入れる事になってるからです。
そこで、まず、storageフォルダに入れた画像などが、
ブラウザ表示できるように、publicフォルダにシンボリックリンクを貼ります。
まず、コマンドプロンプト(Shell/ターミナル)を開きます。
cd laravelプロジェクトフォルダまでのパスを入力、Enterします。
(例 cd D:\XAMPP\htdocs\newsite)
言ってる事がよく分からない場合↓
続いて、
php artisan storage:link
と入力、Enterします。
出来たら、publicフォルダを開いてみてください。
storageというフォルダができてるはずです。
今度は、フォルダを1個戻って、storage\appフォルダを開いてみてください(シンボリックリンクのフォルダではなく、リアルなstorageフォルダの方です)。
そうすると、publicというフォルダが追加されてるはずです。
このstorage\app\publicフォルダ内に入れた画像などは、ブラウザから参照できるようになってます。
では、ここから、実際に画像アップロードフォームを作っていきましょう。
ブレードに画像投稿ボタンを追加
まず、resources\viewsフォルダを開き、以前のチュートリアルで作った form.blade.phpを開きます。
画像赤線、赤枠部分を追記します。
というか、下のコードと<form>~</form>部分を丸ごと入れ替えてしまってもOKです。
コピーしました
コピー
<form action="/newpostsend" method="post" enctype="multipart/form-data">
@csrf
<p>タイトル</p>
<input type="text" name="title" class="formtitle">
<p> </p>
<p>本文</p>
<textarea name="main" cols="40" rows="10"></textarea>
<p> </p>
<p>画像をアップロード</p>
<input type="file" name="post_img">
<p> </p>
<input type="submit" class="submitbtn">
</form>
enctype="multipart/form-data" というのは、フォームから画像、動画、音声などのファイルをアップロードする時に必ず必要なタグです。これがないと、ファイルアップロードはできないようになってます。
そして、 <input type="file" name="post_img"> がファイルをアップロードするためのボタンです。
では、上書き保存してください。
コントローラーに画像保存命令を書く
次にapp\Http\ControllersフォルダのFormController.phpを開きます。
以前作成した public function savenew の所に黄枠部分を追記します。
コピーしました
コピー
// 画像の保存
if($request->post_img){
if($request->post_img->extension() == 'gif' || $request->post_img->extension() == 'jpeg' || $request->post_img->extension() == 'jpg' || $request->post_img->extension() == 'png'){
$request->file('post_img')->storeAs('public/post_img', $post->id.'.'.$request->post_img->extension());
}
}
さらに、最後の return redirectの()の中を('/index')に書き換えてください。
具体的に中にどんな命令が書かれているのかについては、後述します。
ルートを設定する
routes\web.phpに以前のチュートリアルで作った、以下の記述がある事を確認してください。
無い場合は、書き足してください。
画像が保存できてるか確認
では、ブラウザで表示確認するために、先程のコマンドプロンプト(Shell/ターミナル)を開いて、
php artisan serve と入力、Enterします。
言ってる事がよく分からない、うまくいかない場合↓
ブラウザを開いて、http://localhost:8000/create にアクセスしてください。
画像アップロードボタンが追加されたフォームがでてくるはずです。
では、実際に、そこに適当な文章+適当な画像を添付して、送信してみてください。
送信すると、/indexページに転送されたと思います。
この時点では、indexページのブレードをイジってないので、画像は表示されてないはずですが、
画像のアップロード自体は成功してるはずです。
では、どこに画像は保存されているのか?
冒頭で解説したフォルダ、つまり、public\storageフォルダです。
post_imgというフォルダが自動的に作成され、その中に先程アップロードした画像が入っています。
もし、この画像に直接ブラウザからアクセスしたい場合は、
http://localhost:8000/storage/post_img/35.jpeg
のように入力すれば、表示できます。
画像アップロードの仕組みを解説
では、どういう仕組みで、画像がアップロードされるのかを見ていきましょう。
まず、form.blade.phpのフォームの画像アップロードボタン(赤線)にこう書きました。
name="post_img" これがフォルダの名前(name)として指定してあります。
それを水色線の/newpostendというURLに送信してます。
送信されたデータは、web.phpを通して、FormControllerのsavenewメソッドに渡されます。
で、コントローラーのfunction savenew なんですが、
本来なら、この水色線の1行だけで、画像の保存自体はできます。
request->file('post_img')、つまりname="post_img"から送られたファイルをstore(保存)すると記述し、storageのpublic/post_imgフォルダにその画像を保存すると指定しています。
ただ、実際に先程コントローラーに書いたコードはもっともっと長ったらしいコードだったはずです。
というのも、これだけだと、
- 画像の名前がランダムな英字になってしまう
- どんな拡張子のファイルでも保存されてしまう
という問題があるからです。
下の画像のように、ランダムな英字の名前になってしまうと、
実際にサイトに表示する際に、自動的に表示するのが難しくなります。
また、もう一つ問題なのは、拡張子、つまり、jpeg, gif, png といった画像を今回は保存するわけですが、このファイルアップロード、実は.txt, .pdf .mp3 .exe などなど、どんなファイルでもアップロードできてしまいます。
なので、どんなものでもアップロードOKにしてしまうと、変な話、危険なマルウェアプログラムを送信されてしまうなんて事もありえます。
それはまずいので、それを防ぐための記述が必要という事です。
画像に任意の名前を付けて保存
まず、画像に任意の名前を付けて保存する方法ですが、
$request->file('post_img')->storeAs('public/post_img', "xxxx.jpeg");のように、する事で、任意の名前が付けられるようになっているのですが、
今回は、フォームから投稿すると自動的にデータベースに付くID番号を、画像の名前にします。
そうする事で、投稿ID3の記事には、3.jpgが表示されるといった具合に、自動的に画像を表示させる事が可能になります。
なので、まず、下のような記述になります。
ファイル名を、$post->id にし、その後ろにドットを付けて、元のファイルと同じextension(jpg, png, gif)にするとなってます。
画像以外のファイルは保存しない
次に画像以外のファイルがアップロードされた場合は、保存しないように記述します。
画像ファイルの拡張子は、jpeg, jpg, png, gif の4つが通常使われます。jpegだけjpgと2種類あるので、その2種類とも指定します。
まず、最初の全てを囲う if($request->post_img) というのは、「もし画像アップロードがあった場合」という意味です。投稿する際にもちろん画像なしで投稿する場合もありますよね。その時にバグが出ないように、最初にこの記述をして、画像アップロードがあった時のみ、続きが実行されるようにしてあります。
次にそのifの中にさらにif文が入ってます。
次のif文は長いですが、|| が「もしくは」という意味があります。
なので、「もしファイルの拡張子がjpeg もしくは jpg もしくは png もしくは gif なら」という記述になってます。
その条件がTrueなら、投稿IDをファイル名にして保存するとなってるわけです。
それ以外の拡張子だったら、スルーする、つまり保存されないわけです。
他にもif文を使わずに、バリデーションを使うという方法もあるよ。
バリデーション用のRequest.phpに、上のように書き込む事で、画像以外の拡張子ファイルはエラーが出るように設定する事も可能なんだ。
詳しい使い方はバリデーションのチュートリアルを参考にして↓
さて、一通り画像アップロードまではできたわけですが、
ここから今度はこの画像をブレードに表示するために条件を書いていきます。
画像を呼び出し、ブレードに表示する
では、今度は、resources\viewsフォルダを開いて、
post.blade.phpを開いてください。
以前のチュートリアルで記事一覧を表示するために作ったPHPファイルです。
そして、<div class="content">内に黄枠部分を追記します。
コピーしました
コピー
@if(file_exists(public_path().'/storage/post_img/'. $datas->id .'.jpg'))
<img src="/storage/post_img/{{ $datas->id }}.jpg">
@elseif(file_exists(public_path().'/storage/post_img/'. $datas->id .'.jpeg'))
<img src="/storage/post_img/{{ $datas->id }}.jpeg">
@elseif(file_exists(public_path().'/storage/post_img/'. $datas->id .'.png'))
<img src="/storage/post_img/{{ $datas->id }}.png">
@elseif(file_exists(public_path().'/storage/post_img/'. $datas->id .'.gif'))
<img src="/storage/post_img/{{ $datas->id }}.gif">
@endif
なんじゃこりゃ?って感じですが、実際は単純にjpeg, jpg, png, gifの拡張子で分けてるだけです。
file_exists というのが、「ファイルが存在する」という意味なので、「もしpublic path(画像の一般公開用URL)に この記事のid番号.jpg が存在するなら、それを表示してください」
というのを、拡張子ごとにelseif で書いています。
こうする事で、例えば、記事ID3を表示する場合は、3.jpg, 3.jpeg, 3.png, 3.gif のどれかがないか探していき、あれば、それを表示するという条件を作っているんです。
実際にブラウザで表示を確認してみよう!
では、今度はブラウザで http://localhost:8000/index へアクセスしてください。
こんなふうに画像が表示されたはずです(といっても、CSSの関係で、文字が中央寄りになっていたりと若干この画像とは違うかもしれません)。
public\css\style.cssを開いて
.content{
text-align: left;
width:690px;
/* min-height:500px; */
background-color: #ffffff;
border: 5px solid #707aff;
border-radius: 10px;
margin: 20px;
padding: 20px;
}
img{
padding: 10px 0;
}
この太字部分を変更、追記すると、同じレイアウトになると思います。
というわけで、画像のアップロードはこんな具合に達成する必要があるという事です。
画像アップロード自体はとても簡単に達成できますが、
それを自動的にサイトに表示しようとなると、ひと工夫必要になってきます。
次のチュートリアルでは、フォームに禁止文字を設定するバリデーションの作り方をやっていきます。
→Laravel6チュートリアル:バリデーション(入力禁止文字)をフォームに実装しよう!