Laravelで画像のアップロード方法を、確認画面を挟んで行うコードを組んでみました。
また、この記事では以下の方法がわかります。
- 「Storage」クラスを利用した画像をアップロードする方法
- 1ユーザー1画像のアップロードで、画像のアップロードが必須ではない処理
- 確認画面を挟んで画像を保存する
一つずつ細かく解説していきます。
最後に「使用したファイル」をまとめて紹介しているので、解説が不要な方はそちらを参考にしてみてください。
【事前準備】テーブルとmodelファイルの作成
まずは事前準備として、DBのテーブルの用意とmodelファイルの準備を行います。
今回使用するテーブル構成
名前 | タイプ | その他設定 |
id | int | 主キー |
name | varchar(10) | – |
extension | varchar(5) | – |
created_at | timestamp | – |
updated_at | timestamp | – |
マイグレーションファイルの作成
php artisan make:migration create_profiles_table
上記コマンドでマイグレーションファイルを作成します。
作成したファイルの中身を、作成したいテーブルの構成で記述します。
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
$table->increments('id')->comment('主キー');
$table->string("name", 10)->comment('ユーザー名');
$table->string("extension", 5)->comment('画像の拡張子');
$table->timestamps();
});
}
マイグレーションファイルの実行
php artisan migrate
マイグレーションファイルを実行して、「profiles」テーブルの作成ができたことを確認してください。
modelファイルの作成
php artisan make:model profile
次に、「profiles」テーブルを登録更新するためにmodelファイルを用意します。
上記のコマンドを実行して、modelファイルを作成しましょう。
作成したmodelファイルを以下のように変更します。
protected $fillable = [
'name',
'extension',
];
「protected $fillable」で登録更新ができるカラムを指定します。
今回は「name」と「extension」の2つの記述だけで問題ありません。

これで事前準備は完了です!いよいよ画像をアップロードする画面を作成していきましょう!
ルーティングの設定ファイルの編集
まず、今回必要な画面は以下の3つです。
- 入力画面(初期表示)
- 確認画面
- 完了画面
ルーティングの設定ファイル(web.php)に上記画面をアクセスするための設定を行いましょう。
Route::get('/image_upload', 'App\Http\Controllers\ImageUploadController@index');
Route::POST('/image_upload_confirm', 'App\Http\Controllers\ImageUploadController@confirm');
Route::POST('/image_upload_complete', 'App\Http\Controllers\ImageUploadController@complete');
では、各画面ごとにソースを書いていきます。
画像アップロードの「入力画面」の用意
手順ごとに画面やソースを紹介していきます。
コントロールファイルの作成と編集
php artisan make:controller ImageUploadController
最初に、上記コマンドを実行してコントロールファイルを用意します。
まずは初期表示の「入力画面を表示する」ようにコードを記述しましょう。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; // 画像の保存に使用するStorageクラス
use Illuminate\Support\Facades\DB; // DBの保存に使用
use Illuminate\Support\Facades\Log; // エラーが発生した場合のログの保存に使用
use App\Models\profile; // DB「profiles」テーブルにデータを登録する際に使用
class ImageUploadController extends Controller
{
// 入力画面の表示
public function index()
{
return view('image_upload');
}
}

上記の「use」で、後々使用する定義の記述も忘れずに書いておきましょう。
ビューファイルの作成
ファイル名は「image_upload.blade.php」で準備します。
<form action="{{ url('image_upload_confirm') }}" enctype="multipart/form-data" method="post">
{{ csrf_field() }}
ユーザー名:<br>
<input type="text" name="name" value="{{ old('name') }}"><br>
@if($errors->has('name'))
@foreach($errors->get('name') as $message)
{{ $message }}<br>
@endforeach
@endif
<br>
プロフィール画像:<br>
<input type="file" name="image"><p>
@if($errors->has('image'))
@foreach($errors->get('image') as $message)
{{ $message }}<br>
@endforeach
@endif
<br>
<input type="submit" value="写真をアップロード">
</form>
ここでのポイントは、以下のソースです。
<form action="{{ url('image_upload_confirm') }}" enctype="multipart/form-data" method="post">
本来のformタグに「enctype=”multipart/form-data」なるものは記述しません。
「enctype」をつけることによりHTTPリクエストのパラメータが変化し、画像やファイルなど添付してデータを取得することができるようになります。

もしformタグに「enctype=”multipart/form-data」を書いていないと、添付ファイルを受け取ることができません。
<input type=”file”>タグを使用する場合は、必ずformタグに「enctype=”multipart/form-data」を付け足すことを忘れないようにしましょう!
ブラウザで画面を表示してみて、以下のような表示になっていればOKです。

画像アップロードの「確認画面」の用意
次に確認画面の準備を行っていきます。
コントロールファイルの編集
先ほど作成したコントロールファイルに、追記します。
public function confirm(Request $request)
{
$request->validate([
'name' => ['required','max:10'],
'image' => ['image','mimes:jpeg,png,jpg,gif'],
]);
$name = $request->name;
$image = $request->file('image');
if ($image) {
// 拡張子の取得
$extension = $image->getClientOriginalExtension();
// 新しいファイル名を作る(ランダムな文字数とする)
$new_name = uniqid() . "." . $extension;
// 一時的にtmpフォルダに保存する
$image_path = Storage::putFileAs(
'tmp', $request->file('image'), $new_name
);
} else {
$new_name = 'noimage.jpg';
$extension = '0';
$image_path = 'noimage.jpg';
}
return view('image_upload_confirm', compact('image_path','extension','name'));
}
上から解説していきます。
まずはバリデーションですね。
$request->validate([
'name' => ['required','max:10'],
'image' => ['image','mimes:jpeg,png,jpg,gif'],
]);
入力項目である「ユーザー名」と「画像」のバリデーションを行っています。
「image」は添付ファイルが画像ファイルかどうかのチェックです。
「mimes」は拡張子が指定されたものかチェックしています。

「mines:拡張子」ですね。
今回は「jpeg、png、jpg、gif」の4つの拡張子のみOKとしています。
バリデーションの次は、添付ファイルをあれこれしています。
if ($image) {
// 拡張子の取得
$extension = $image->getClientOriginalExtension();
// 新しいファイル名を作る(ランダムな文字数とする)
$new_name = uniqid() . "." . $extension;
// 一時的にtmpフォルダに保存する
$image_path = Storage::putFileAs(
'tmp', $request->file('image'), $new_name
);
} else {
$new_name = 'noimage.jpg';
$extension = '0';
$image_path = 'noimage.jpg';
}
今回、画像ファイルの添付は必須ではないため、添付しないパターンも記述しています。
添付しない場合は、「notimage.jpg」ファイルを表示するようにします。
画像ファイルの添付があった場合は、一時的にファイルの保存を行う処理にしました。
そこで利用したのが「Storage」クラスです。

「Storage::」クラスを利用すると、「storage\app」フォルダ配下に保存してくれたり、読み込んだりしてくれます。
「Storage::putFileAs」はファイル名を手動で指定できます。
第一引数に保存先フォルダ、第二引数に保存するファイルデータ、第三引数にファイル名を記入します。
「Storage::putFileAs」の返り値は、保存したパス(ファイル名付き)なので今回活用しました。
今回、ファイル名を指定した理由は、
もし万が一にも「同時刻に」「別のユーザーが」「同ファイル名をアップロードした」場合、後から処理した人の画像に上書きされてしまいかねないからです。
ノーイメージ画像の用意
「storage\app」配下に、ノーイメージ画像を保存しておきます。
ファイル名は「notimage.jpg」にしておきましょう!

私はフリー素材から用意しました。
ビューファイルの作成
確認画面のビューファイル「image_upload_confirm.blade.php」を用意します。
<form action="{{ url('image_upload_complete') }}" method="post">
{{ csrf_field() }}
ユーザー名:{{ $name }}
<input type="hidden" name="name" value="{{ $name }}">
<br>
プロフィール画像:<br>
<img src="./../storage/app/{{ $image_path }}" alt="" width="40%">
<input type="hidden" name="image_path" value="{{ $image_path }}">
<input type="hidden" name="extension" value="{{ $extension }}"><br>
<input type="submit" value="完了">
</form>
imgタグで一時的に保存した画像ファイルを表示します。
<img src="./../storage/app/{{ $image_path }}" alt="" width="40%">
では実際にブラウザで操作してみます。
画像を添付した場合は、以下のように表示されます。
(画像は自分が選択したファイルになります)

画像を添付しなかった場合は、用意したノーイメージ画像が表示されます。


「storage\app\tmp」配下にランダムな文字のファイル名で、添付した画像が保存されていることも、確認しておきましょう!
画像アップロードの完了画面の用意
最後に完了画面の準備を行っていきます。
コントロールファイルの編集
先ほど作成したコントロールファイルに、追記します。
public function complete(Request $request)
{
try {
DB::beginTransaction();
$data = profile::create([
"name" => $request->name,
"extension" => $request->extension,
]);
DB::commit();
// 新しいファイル名を作る
// この場合、IDをファイル名にしている↓
$new_name = $data->id . '.' . $request->extension;
if($request->image_path == 'noimage.jpg'){
// ノーイメージ画像をコピーして、ユーザー毎の画像を用意する場合はこのコード↓
// Storage::copy($request->image_path, 'img/'.$new_name);
// 後々、Webサイトの改装時にノーイメージ画像を変更したい場合があるので、
// 各ユーザー毎にノーイメージ画像を量産すると地獄を見るのでおすすめできません
// 今回の場合、DBの「拡張子」カラムに「0」が登録されるように作ったので、
// 拡張子が0ならノーイメージ画像を表示判定ができる仕様にしてみました
} else {
// 一時保存のtmpから本番の格納場所imgへ移動
Storage::move($request->image_path, 'img/'.$new_name);
}
return view('image_upload_complete');
} catch (\Exception $e) {
DB::rollback();
Log::error($e);
print_r('エラーが発生しました。');
exit;
}
}
上から解説していきます。
$data = profile::create([
"name" => $request->name,
"extension" => $request->extension,
]);
ここではDBにデータの登録を行っています。
後ほど画像はユーザーごとに付与するIDと同じ名前を付与するので、「extension」には拡張子のみ保存します。
$new_name = $data->id . '.' . $request->extension;
ここでファイル名を指定しています。例えばIDが「1」で、拡張子が「gif」の画像をアップロードしていた場合は「1.gif」で登録されるようにしています。
Storage::move($request->image_path, 'img/'.$new_name);
「Storage::move」はファイルの移動です。
第一引数に元ファイルのパス、第二引数に移動先パスとファイル名を記載します。
今回、移動先として保存するフォルダは「storage\app\img」配下にしています。

「Storage::copy」を利用すると、元ファイルをコピーできます。
今回の場合、一時保存は残しておく必要がないので「Storage::move」にしました。
ビューファイルの作成
完了画面のビューファイル「image_upload_complete.blade.php」を用意します。
画像のアップロードが完了しました。
ビューファイルは特に必要な表示はないので、完了文言だけ記入しました。
実際に操作してみましょう!

完了の文言がでますね。
保存先の「storage\app\img」配下を見てみます。

反対に「storage\app\tmp」配下は画像が消えているはずです。
これで、確認画面を挟んでの画像アップロードの処理は完了です!
今回のファイルのまとめ
今回は7つのファイルを使用しました。
<?php
use Illuminate\Support\Facades\Route;
// 画像アップロード用
Route::get('/image_upload', 'App\Http\Controllers\ImageUploadController@index');
Route::POST('/image_upload_confirm', 'App\Http\Controllers\ImageUploadController@confirm');
Route::POST('/image_upload_complete', 'App\Http\Controllers\ImageUploadController@complete');
さいごに
今回は確認画面を挟んでの画像をアップロードする方法を紹介しました。
画像のアップロード画面を作る際の参考にしてもらえれば幸いです。
懸念としては一次保存のファイルが増えかねないことです。
「確認画面まで進んだけど、入力内容を直したくて戻った場合」一時保存したファイルは残り続けます。
タイミングを見て自動なり手動なりで消すしかないのかな、とも思います。