LoginSignup
2
5

More than 1 year has passed since last update.

Laravelの画像アップロード処理 - まとめ -

Posted at

はじめに

画像の保存の処理で苦戦したため、記録として残します。
少しでもつまづいている方の参考になれば嬉しいです。

条件

mac OS "11.2.3 Big Sur"
Laravel Framework "6.20.27"
PHP "8.0.7"
nginx "1.18"
mysql "8.0" 

画像の保存方法

・DBには画像のパス(画像がどこに保存されているのか)のみを保存

database
$table->string('cooking_img_file')
// パス名を保存するので、String型

・画像本体はStorageに保存

保存の流れ

大まかな保存の処理の流れは以下のような感じです。

  1. フォームに画像を送付して送信

  2. 画像本体はStorageに保存

3. DBには画像のパス(画像がどこに保存されているのか)のみを保存

実装の流れ

  1. 画像データの保存先を作成
  2. シンボリックリンク
  3. テーブルにカラムの追加
  4. 画像の保存処理

実装

1. 画像データの保存先を作成

画像本体は、Storage/app/publicに保存されるようにします。
今回は、publicディレクトリの中にさらに料理写真を保存するrecipeディレクトリを作成しています。
スクリーンショット 2021-06-18 10.26.08.png
上記のように、画像データがどんどん保存されていきます。

そして、DBにはここまでの道のり、つまりstorage/app/public/recipes/....の部分を保存するようにします。

2. シンボリックリンク

画像を保存するディレクトリは作成しましたが、先ほど説明したstorage/app/publicは、ウェブ上に公開されない非公開ディレクトリであるため、このままでは保存した画像を画面上で見ることはできません。
そのため、公開ディレクトリであるpublicディレクトリから非公開ディレクトリにアクセスして画面上に画像を表示させるようにします。

スクリーンショット 2021-06-24 22.39.19.png

赤枠が公開ディレクトリ、青枠が非公開ディレクトリとなっています。
この青枠の中に保存されている画像を赤枠からアクセスできるようにして、保存されている画像をウェブ上で見れるようにします。

それを行うには、シンボリックリンクというものでこの赤枠青枠を繋げないといけません。

そのリンクを貼る方法は簡単で、以下のコマンドをターミナルでうつだけです。

# php artisan storage:link

すると、赤枠の中にある、storageファイルに矢印がつきます。
これでリンクが貼られたことになります。

3. テーブルにカラムの追加

1. 画像を保存するためテーブルに画像のファイルパスを保存するカラムを追加する

$table->string('cooking_img_file')->nullable();

今回、画像データは空でもOKという条件で実装しているためnullable()を入れています。

3. Blade

<form method="POST" action="{{ route('recipes.store') }}" enctype="multipart/form-data">

  <input type="file" name="cooking_img_file" class="d-none" accept="image/png,image/jpeg,image/gif" />

① form に enctype = "multipart/form-data"を記述する。
これを記述しないと画像がアップロードされない。

② input: type = "file"
送られてくるデータはfileとなるため.

4. 画像の保存処理

アップロードされた画像の保存処理の全体像は以下のようになります。

    public function store(RecipeRequest $request, Recipe $recipe)
    {
        $recipe->fill($request->all());

        // ❶ アップロードされたファイル情報を $recipe_image に格納
        $recipe_image = $request->file('cooking_img_file');

        // 画像保存処理
        if($recipe_image) {
            // ❷ `recipe`というディレクトリに $recipe_image のデータを保存する
            $path = Storage::disk('public')->putFile('recipes', $recipe_image);
            // ❸ basenameで $path の最後の部分(=ファイルの名前のみ)を取得
            $recipeFileName = basename($path);
            // ❹ 取得したファイルの名前のみをDBに保存する
            $recipe->cooking_img_file = $recipeFileName;
        } else {
            $path = null;
        }

        $recipe->save();

        // 以下省略
    }

❶ アップロードされた画像の取得

以下のコードで、アップロードされた画像の情報を取得でき、それを$recipe_imageに格納しています。

$recipe_image = $request->file('cooking_img_file');

file()には、第一引数にinputタグのname属性の値を指定します。
(今回はinputタグに、name="cooking_img_file"と記述しています。)

画像保存処理

「画像の添付は必須ではない」という設定をしているため、if文で画像が添付されている時とされていない時で条件分岐しています。

// 条件分岐

       // 画像がアップロードされている時
        if($recipe_image) {
            $path = Storage::disk('public')->putFile('recipes', $recipe_image);
            $recipeFileName = basename($path);
            $recipe->cooking_img_file = $recipeFileName;
       // 画像がアップロードされていない時
        } else {
            $path = null;
        }

❷ 画像を保存する場所の指定

$path = Storage::disk('public')->putFile('recipes', $recipe_image);

1つずつ見ていきます。

Storage::disk('public')

Storage::disk('○') → ○にはディスク名を指定。

ディスク名は、デフォルトで以下の3つがconfig/filesystems.phpに用意されています。
今回はユーザーも画像を閲覧できるようにしたいので、diskにはpublicを指定しています。

  1. local:ローカルディスク(非公開)
  2. public:ローカルディスク(公開)
  3. s3:AWS S3
config/filesystems.php
    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
        ],
->putFile('recipes', $recipe_image);

// 第一引数 : 保存先のフォルダ名. 上記では、`Storage/app/public/recipes` になる
// 第二引数 : 保存したいファイル

❸ 画像のパス名

$recipeFileName = basename($path);

// basename : パスの最後にある名前の部分を返す

現時点で$pathには、

スクリーンショット 2021-06-14 18.48.18.png
というように、public/recipes/が含まれているため、basenameで画像のファイル名のみを取り出しDBに保存できるようにします。
この時点で$recipeFileNameには、以下のようにファイル名のみを保存されるようにします。
スクリーンショット 2021-06-14 18.54.49.png

❹ 取得したファイルの名前のみをDBに保存する

❸の処理で取り出した画像のファイル名を$recipeFileNameに格納します。

$recipe->cooking_img_file = $recipeFileName;

そして、画像のファイル名をDBのcooking_img_fileに保存しています。

画像を添付していない場合は、$pathの値にnullが入るようにしています。

        } else {
            $path = null;
        }

これでコントローラー内の画像保存処理は終了です。

おわりに

画像の保存処理は、初めは何をしているのか少しややこしかったですが一つ一つみていくことで少しずつ理解ができてきました。
この調子で頑張っていくぞ!

参考

2
5
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
2
5