確認画面付きで画像をアップロードする方法・Laravel

【Laravel】画像のアップロード!確認画面を挟んで行う方法を解説

Laravelで画像のアップロード方法を、確認画面を挟んで行うコードを組んでみました。

また、この記事では以下の方法がわかります。

  • 「Storage」クラスを利用した画像をアップロードする方法
  • 1ユーザー1画像のアップロードで、画像のアップロードが必須ではない処理
  • 確認画面を挟んで画像を保存する

一つずつ細かく解説していきます。

最後に「使用したファイル」をまとめて紹介しているので、解説が不要な方はそちらを参考にしてみてください。

バージョン9.51.0の「Laravel」で説明します

目次

【事前準備】テーブルとmodelファイルの作成

まずは事前準備として、DBのテーブルの用意とmodelファイルの準備を行います。

今回使用するテーブル構成

名前タイプその他設定
idint主キー
namevarchar(10)
extensionvarchar(5)
created_attimestamp
updated_attimestamp
テーブル名「profiles」

マイグレーションファイルの作成

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つです。

  1. 入力画面(初期表示)
  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');

さいごに

今回は確認画面を挟んでの画像をアップロードする方法を紹介しました。

画像のアップロード画面を作る際の参考にしてもらえれば幸いです。

懸念としては一次保存のファイルが増えかねないことです。
「確認画面まで進んだけど、入力内容を直したくて戻った場合」一時保存したファイルは残り続けます。

タイミングを見て自動なり手動なりで消すしかないのかな、とも思います。

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