#はじめに
Laravelで画像アップロードする方法を色々試してみたので簡単にまとめました。
最終的にHerokuにデプロイして使えるようにします。
今回Laravelのバージョンは5.8です。Laravel 5.8 インストール
.envファイル修正後、以下のコマンドでモデルとコントローラー、マイグレーションを一気に作成します。
$ php artisan make:model Image -m -c -r
-m
で作成したモデルに関連したマイグレーションファイルを同時に作成し、さらに-c
でコントローラーも作成します。-r
はResourcefulなアクションを自動で定義したコントローラーを作成してくれます。
#Contents
以下の流れで説明していきます。
#ファイルシステムを利用して画面に表示
マイグレーションファイルは以下のようにします。
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('image');
$table->timestamps();
});
}
次にフォームをサクッと作成します。
<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]
と結びついています。
そして、コントローラーを実装。必要な部分だけのせます。
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の追加ですね。
<?php
Route::get('/', 'ImageController@index');
Route::post('/', 'ImageController@store')->name('image.store');
完了です。でもこのままだとLaravelのデフォルトでは storage/app/public/ 配下はWebからのアクセスが許可されていません。
Webからのアクセスを許可するために以下のコマンドを打ちます。
$ php artisan storage:link
あとはローカルで動作確認してみてください。以下の画像のようにできているかと思います。
できてますね!よーし、Herokuにデプロイだ!としたいところなんですが、これだとダメなんです...。
Herokuのdynoは1日1回再起動するらしく、アップロードしたファイルも全消去されて表示されなくなります
てことで、別の方法を考えます。
#DBに画像のバイナリデータを直接入れて表示(base64)
今回のマイグレーションファイルは以下のようにします。
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->bigIncrements('id');
$table->longText('image'); //ここが変わった
$table->timestamps();
});
}
次にフォームを少し変えます。
<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でエンコードされた画像を表示するという記法になります。
そしてコントローラーも変えていきましょう。今回も必要な部分だけのせます。
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は変更ないので早速ローカルで動作確認してみてください。以下の画像のようにできているかと思います。
phpMyAdminの方も確認しました。めちゃくちゃ長い文字列が格納されてました。。。
これならHerokuにデプロイしても画像はしっかり表示されます。
...ですが、base64でエンコードするとサイズがその分増えます。そうするとHerokuの無料枠のデータベースサイズは5 MBなので大きいサイズの画像をたった2枚アップロードするだけで終了です。
なら課金すんべ!と思って料金調べました。→ClearDB MySQL - Add-ons - Heroku Elements
ん〜〜〜高スギィ!!!!
てことで、別の方法を考えます。
#S3に画像アップロードして表示
Amazon Simple Storage Service(Amazon S3)を利用するにあたって、こちらの方の記事がとても参考になります。これでS3へアップロードできるようにします。(S3の環境情報を登録まで)
※追加で「ブロックパブリックアクセス (バケット設定)」の部分のチェックボックスは全て外しといてください。ブロックすると画像が表示されませんでした。また、システムのアクセス許可の管理は「付加しない」で良いと思います。
その後、マイグレーションファイルを以下のようにします。
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('image');
$table->timestamps();
});
}
そしてまたフォームを少し変えます。
<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
最後にコントローラーを変えていきましょう。今回も必要な部分だけのせます。
<?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によるアクセスが可能となります。
以上で実装完了です。まず、ローカルで動作確認してみましょう!以下の画像のようになっていると思います。
phpMyAdminの方も確認しました。imageテーブルにファイルのURLが格納されていますね。
しっかり実装できていますね。ではHerokuにデプロイしていきましょう!!
Herokuについては、こちらの方の記事がとても参考になります。
#画像アップロードするにあたって躓いたところ
一部の画像だけなぜかアップロードができない現象が起きました。
その一部の画像はサイズがでかく、php.iniがデフォルトでupload_max_filesizeが2Mになっていることでした。
ここはHerokuを使ってLaravelの画像アップロードする際に気をつけないといけない部分ですね。
てことで、php.ini の設定を変えましょう
php.ini の設定を適用するためには public ディレクトリに.user.ini
というファイルを作成し、そこに以下の設定を書き込みます。
post_max_size = 20M
upload_max_filesize = 20M
#さいごに
ここまで実装できるまでに、多くのサイトを参考にさせて頂きました。有難う御座います。
最後の「S3に画像アップロードして表示」を使って「みんなで共有!ランチマップ」を自作してみました!
ぜひ利用してみて下さい!