76
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Laravelを使った画像アップロード実装法3選

Last updated at Posted at 2019-08-08

#はじめに
Laravelで画像アップロードする方法を色々試してみたので簡単にまとめました。
最終的にHerokuにデプロイして使えるようにします。
今回Laravelのバージョンは5.8です。:point_right:Laravel 5.8 インストール

.envファイル修正後、以下のコマンドでモデルとコントローラー、マイグレーションを一気に作成します。


$ php artisan make:model Image -m -c -r

-mで作成したモデルに関連したマイグレーションファイルを同時に作成し、さらに-cでコントローラーも作成します。-rはResourcefulなアクションを自動で定義したコントローラーを作成してくれます。

#Contents
以下の流れで説明していきます。

  1. ファイルシステムを利用して画面に表示
  2. DBに画像のバイナリデータを直接入れて表示(base64)
  3. S3に画像アップロードして表示

#ファイルシステムを利用して画面に表示
マイグレーションファイルは以下のようにします。

2019_08_06_131550_create_images_table.php
public function up()
{
    Schema::create('images', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('image');
        $table->timestamps();
    });
}

次にフォームをサクッと作成します。

index.blade.php
<form method="POST" action="{{ route('image.store') }}" enctype="multipart/form-data">
@csrf
    <input id="image" type="file" name="image">
    <button type="submit">
       アップロード
    </button>
</form>
@foreach($images as $image)
    <div>
        <img src="{{ asset('storage/' . $image->image) }}" alt="image" style="width: 30%; height: auto;"/>
    </div>
@endforeach

ここで重要なのはenctype="multipart/form-data"ですね。ファイルをアップロードをする場合に必ず必要になります。
あとは、@foreach($images as $image)ですが、この$imagesは以下のControllerのindexメソッドの['images'=>$images]と結びついています。

そして、コントローラーを実装。必要な部分だけのせます。

ImageController.php
class ImageController extends Controller
{
    public function index()
    {
        $images = Image::all();
        return view('index', ['images'=>$images]);
    }

    public function store(Request $request)
    {
        $image = new Image();
        $uploadImg = $request->image;
        if($uploadImg->isValid()) {
            $filePath = $uploadImg->store('public');
            $image->image = str_replace('public/', '', $filePath);
        }
        $image->save();
        return redirect('/');
    }
}

最後にRouteの追加ですね。

web.php
<?php
Route::get('/', 'ImageController@index');
Route::post('/', 'ImageController@store')->name('image.store');

完了です。でもこのままだとLaravelのデフォルトでは storage/app/public/ 配下はWebからのアクセスが許可されていません。
Webからのアクセスを許可するために以下のコマンドを打ちます。


$ php artisan storage:link

あとはローカルで動作確認してみてください。以下の画像のようにできているかと思います。
スクリーンショット 2019-08-07 18.03.58.png

phpMyAdminでも確認してみました。
スクリーンショット 2019-08-07 18.04.12.png

できてますね!よーし、Herokuにデプロイだ!としたいところなんですが、これだとダメなんです...。
Herokuのdynoは1日1回再起動するらしく、アップロードしたファイルも全消去されて表示されなくなります:angel:

てことで、別の方法を考えます。:point_down:
#DBに画像のバイナリデータを直接入れて表示(base64)
今回のマイグレーションファイルは以下のようにします。

2019_08_06_131550_create_images_table.php
public function up()
{
    Schema::create('images', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->longText('image'); //ここが変わった
        $table->timestamps();
    });
}

次にフォームを少し変えます。

index.blade.php
<form method="POST" action="{{ route('image.store') }}" enctype="multipart/form-data">
@csrf
    <input id="image" type="file" name="image">
    <button type="submit">
       アップロード
    </button>
</form>
@foreach($images as $image)
    <div>
        <img src="data:image/png;base64,{{ $image->image }}" alt="image" style="width: 30%; height: auto;">
    </div>
@endforeach

imgタグのsrc属性にdata:image/png;base64,をつけてあげることによって、base64でエンコードされた画像を表示するという記法になります。

そしてコントローラーも変えていきましょう。今回も必要な部分だけのせます。

ImageController.php
class ImageController extends Controller
{
    public function index()
    {
        $images = Image::all();
        return view('index', ['images'=>$images]);
    }

    public function store(Request $request)
    {
        $image = new Image();
        $image->image = base64_encode(file_get_contents($request->image));
        $image->save();
        return redirect('/');
    }
}

storeメソッドが少し短くなりましたね。
受け取った画像データをbase64_encodeでエンコードしてDBに格納しています。
それを上のindex.blade.phpのimgタグのところで表示している流れです。

web.phpは変更ないので早速ローカルで動作確認してみてください。以下の画像のようにできているかと思います。
スクリーンショット 2019-08-07 23.29.49.png

phpMyAdminの方も確認しました。めちゃくちゃ長い文字列が格納されてました。。。
スクリーンショット 2019-08-07 23.30.24.png

これならHerokuにデプロイしても画像はしっかり表示されます。
...ですが、base64でエンコードするとサイズがその分増えます。そうするとHerokuの無料枠のデータベースサイズは5 MBなので大きいサイズの画像をたった2枚アップロードするだけで終了です。
D-PxjlwU0AAB0DU.jpeg

なら課金すんべ!と思って料金調べました。→ClearDB MySQL - Add-ons - Heroku Elements

ん〜〜〜高スギィ!!!!

てことで、別の方法を考えます。:point_down:
#S3に画像アップロードして表示
Amazon Simple Storage Service(Amazon S3)を利用するにあたって、こちらの方の記事がとても参考になります。これでS3へアップロードできるようにします。(S3の環境情報を登録まで)

※追加で「ブロックパブリックアクセス (バケット設定)」の部分のチェックボックスは全て外しといてください。ブロックすると画像が表示されませんでした。また、システムのアクセス許可の管理は「付加しない」で良いと思います。
スクリーンショット 2019-08-08 4.34.28.png

その後、マイグレーションファイルを以下のようにします。

2019_08_06_131550_create_images_table.php
public function up()
{
    Schema::create('images', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('image');
        $table->timestamps();
    });
}

そしてまたフォームを少し変えます。

index.blade.php
<form method="POST" action="{{ route('image.store') }}" enctype="multipart/form-data">
@csrf
    <input id="image" type="file" name="image">
    <button type="submit">
       アップロード
    </button>
</form>
@foreach($images as $image)
    <div>
        <img src="{{ $image->image }}" alt="image" style="width: 30%; height: auto;"/>
    </div>
@endforeach

最後にコントローラーを変えていきましょう。今回も必要な部分だけのせます。

ImageController.php
<?php

namespace App\Http\Controllers;

use App\Image;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class ImageController extends Controller
{
    public function index()
    {
        $images = Image::all();
        return view('index', ['images'=>$images]);
    }

    public function store(Request $request)
    {
        $image = new Image();
        $uploadImg = $image->image = $request->file('image');
        $path = Storage::disk('s3')->putFile('/', $uploadImg, 'public');
        $image->image = Storage::disk('s3')->url($path);
        $image->save();
        return redirect('/');
    }
}

今回はstoreメソッドでStorageファサードを使用しているので、必ずuse Illuminate\Support\Facades\Storage;を追加してあげてください。

また、putFileの第一引数はバケット内の階層です。フォルダを作成しなければ上と同じでOKです。
例でバケット内に”Test”フォルダを作成しその中に画像を入れたいとすると、第一引数は'/test'としないといけません。
第二引数は受け取った画像が入った変数。
第三引数は'public'で他からのアクセスを全般的に許可することとなり、ファイルのURLによるアクセスが可能となります。

以上で実装完了です。まず、ローカルで動作確認してみましょう!以下の画像のようになっていると思います。
スクリーンショット 2019-08-08 16.45.43.png

phpMyAdminの方も確認しました。imageテーブルにファイルのURLが格納されていますね。
スクリーンショット 2019-08-08 16.46.09.png

Amazon S3のバケット内も確認してみました。
スクリーンショット 2019-08-08 16.45.55.png

しっかり実装できていますね。ではHerokuにデプロイしていきましょう!!
Herokuについては、こちらの方の記事がとても参考になります。
#画像アップロードするにあたって躓いたところ
一部の画像だけなぜかアップロードができない現象が起きました。
その一部の画像はサイズがでかく、php.iniがデフォルトでupload_max_filesizeが2Mになっていることでした。
ここはHerokuを使ってLaravelの画像アップロードする際に気をつけないといけない部分ですね。
てことで、php.ini の設定を変えましょう:point_down:

php.ini の設定を適用するためには public ディレクトリに.user.iniというファイルを作成し、そこに以下の設定を書き込みます。

.user.ini
post_max_size = 20M
upload_max_filesize = 20M

#さいごに
ここまで実装できるまでに、多くのサイトを参考にさせて頂きました。有難う御座います。
最後の「S3に画像アップロードして表示」を使って「みんなで共有!ランチマップ」を自作してみました!
ぜひ利用してみて下さい!

76
60
3

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
76
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?