Help us understand the problem. What is going on with this article?

Laravel 6 画像のアップロード

環境

・Laravel 6.18.43
・PHP 7.3.11
・ブラウザ:クローム

画面構成

フォーム

ファイルと商品名を入力します。

スクリーンショット 2020-11-20 14.07.40.png

確認画面

確認画面を表示します。
input->confirmの際に一度Storage/app/public内のtempディレクトリに仮保存し、
confirm->completeの際にStorage/app/public内のproductImageディレクトリに移動します。

スクリーンショット 2020-11-20 14.17.53.png

完了画面

完了画面です。

スクリーンショット 2020-11-20 14.20.30.png

ソース

まずはルーティングを作成。

web.php
Route::get('/image_input', 'ImageController@getImageInput');
Route::post('/image_confirm', 'ImageController@postImageConfirm');
Route::post('/image_complete', 'ImageController@postImageComplete');

続いてフォームの作成。
formタグ内でenctype="multipart/form-dataを記述する必要があるようです。

imege_input.blade.php
<form action="image_confirm" method="post" enctype="multipart/form-data" id="form">
  @csrf
  <p>ファイル</p>
  <input type="file" name="imagefile" value=""/><br /><br />

  <p>商品名</p><br />
  <input type="text" name="product_name" size="50" value="{{ old('name') }}"/><br /><br />

  <input type="submit" name="confirm" id="button" value="確認" />
</form>

確認画面

image_comfirm.blade.php
<form action="image_complete" method="post">
  @csrf
  <table border="1">
    <tr>
      <td>画像</td>
      <td><img src="{{ $data['read_temp_path'] }}" width="200" height="130"></td>
    </tr>
    <tr>
      <td>商品名</td>
      <td>{{ $data['product_name'] }}</td>
    </tr>
  </table>
  <input type="submit" name="action" value="送信" />
</form>

完了画面

image_complete.blade.php
<p>商品のアップロードが完了しました</p>
<img src="{{ $product->path }}" width="200" height="130">

また、DBは次のmigrationファイルを基にして作り、Modelも作成しておきます。

xxxx_xx_xx_xxxxxx_create_products_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('path');
            $table->string('product_name');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('products');
    }
}

コントローラー

フォームから画像と名前を受け取って、画像には一意のファイル名としてtempディレクトリに保存します。
また、sessionに保存して、confirm画面から読み込めるようにします。
useの記述漏れに注意。

imageController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

class ImageController extends Controller
{
    public function getImageInput(){
        return view('image_input');
    }

    public function postImageConfirm(Request $request){

        $post_data = $request->except('imagefile');
        $imagefile = $request->file('imagefile');

        $temp_path = $imagefile->store('public/temp');
        $read_temp_path = str_replace('public/', 'storage/', $temp_path); //追加

        $product_name = $post_data['product_name'];

        $data = array(
            'temp_path' => $temp_path,
            'read_temp_path' => $read_temp_path, //追加
            'product_name' => $product_name,
        );
        $request->session()->put('data', $data);

        return view('image_confirm', compact('data') );
    }

    public function postImageComplete(Request $request) {//getをpostに変更

        $data = $request->session()->get('data');
        $temp_path = $data['temp_path'];
        $read_temp_path = $data['read_temp_path'];

        $filename = str_replace('public/temp/', '', $temp_path);
        //ファイル名は$temp_pathから"public/temp/"を除いたもの
        $storage_path = 'public/productimage/'.$filename;
        //画像を保存するパスは"public/productimage/xxx.jpeg"

        $request->session()->forget('data');

        Storage::move($temp_path, $storage_path);
        //Storageファサードのmoveメソッドで、第一引数->第二引数へファイルを移動

        $read_path = str_replace('public/', 'storage/', $storage_path);
        //商品一覧画面から画像を読み込むときのパスはstorage/productimage/xxx.jpeg"
        $product_name = $data['product_name'];

        $product = new Product;
        $product->path = $read_path;
        $product->product_name = $product_name;

        $product->save();

        return view('image_complete', compact('product'));
    }
}

シンボリックリンク

Laravel で作ったアプリケーションが公開されるとき、公開されるのは一番上の階層にある public ディレクトリのみです。public ディレクトリにファイルや処理のすべてが集約されています。なので、保存した画像も public ディレクトリ内に存在しないとアクセスすることができません。そこでシンボリックリンクを利用して storage ディレクトリにアクセスできるようにします。
Laravelの場合、画像を読み込むディレクトリはpublic/storageディレクトリです。
なのでpublic/storageから、storage/app/public内を読み込めるよう(シンボリックリンクを張る)にします。
保存:storage/app/public
読込:public/storage

シンボリックリンクは Artisan コマンドで作成できます。

php artisan storage:link

これで、storage/app/public内を覗けるようになりました。

参考サイト

Laravelで画像ファイルを保存したい
PHP Laravel 6 画像のアップロード

freedog1
メモ帳
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away