概要
画像アップロードはWeb開発において一般的な機能です。Laravelを使って、var/www/imagesに画像を保存し、データベースで管理する方法を解説します。さらに、画像をWebP形式に変換し、サイズ別に保存する方法も紹介します。
保存場所の作成
Laravelでは、基本的に、storage/app/imagesに画像を保存して、public/storageにシンボリックリンクを張るのが一般的ですが、他のアプリからも参照できるように、/var/www/imagesに保存するようする。
データベースの作成
imagesテーブルを作成し、id、uuid、ファイル名、ファイルパス、サイズを定義する。
モデルの作成
Eloquentモデル Image を作成します。
画像アップロードの実装
- HTMLフォームの作成。
- 画像アップロード処理を行うコントローラーの実装。
- 画像を受け取り、一時的に var/www/images/oldに保存する処理。
画像をWebPに変換
- ntervention Imageライブラリを使用して、画像をWebPに変換。
- 元のサイズ、縦横半分のサイズ、中央を200×200pxにリサイズした画像を生成。
- 変換した画像を var/www/images に保存。
画像情報のデータベースへの保存
変換後の画像ファイルの情報(パス、サイズ等)をデータベースに保存する処理。
画像の表示
保存した画像をWebアプリケーションで表示する方法。
保存場所の作成
ディレクトリの作成
ターミナルから、VPSサーバーにアクセスして、/var/www/imagesディレクトリと、/var/www/images/oldディレクトリを作成します。
sudo mkdir -p /var/www/images/old
Ubuntuで /var/www/images ディレクトリとそのサブディレクトリ var/www/images/old を同時に作成するには、mkdir コマンドに-p オプションを使用します。このオプションは、指定されたディレクトリと必要なすべての親ディレクトリを同時に作成します。
パーミッションの設定
Laravelからアクセスできるように、パーミッションを設定します。
// オーナーの変更
sudo chown www-data:www-data -R /var/www/images
// 権限の変更
sudo chmod 775 -R /var/www/images
データベースの作成
マイグレーションファイルの作成
以下のコマンドを実行して、新しいマイグレーションファイルを生成します。
php artisan make:migration create_images_table
マイグレーションファイルの編集
生成されたマイグレーションファイルを開き( database/migrations ディレクトリ内)、以下のように編集します。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateImagesTable extends Migration
{
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->string('name');
$table->string('path');
$table->integer('size')->nullable();
$table->timestamps();
$table->softDeletes();
// インデックスを追加する。
$table->index('uuid');
});
}
public function down()
{
Schema::dropIfExists('images');
}
}
このコードにより、imaesテーブルに以下のカラムが作成されます:
- id:ユニークな識別子(自動インクリメント)。
- uuid:ユニークなUUID。
- name:画像の名前。
- path:画像の保存パス。
- size:画像のサイズ(バイト単位)。
- created_at とupdated_at:タイムスタンプ。
- deleted_at:論理削除用のタイムスタンプ(ソフトデリート)。
マイグレーションの実行
編集が完了したら、以下のコマンドを実行してマイグレーションを実行します。
php artisan migrate
これで、指定されたプロパティを持つ images テーブルがデータベースに作成されます。ソフトデリートを使用する場合は、対応するEloquentモデルにも SoftDeletesトレイトを使用する必要があります。
モデルの作成
モデルの生成
LaravelのArtisanコマンドラインツールを使用して、Image モデルを生成します。ターミナルまたはコマンドプロンプトで以下のコマンドを実行します。
php artisan make:model Image
モデルの編集
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; // ソフトデリートを使用する場合
class Image extends Model
{
use SoftDeletes; // ソフトデリートを使用する場合
protected $fillable = [
'uuid', 'name', 'path', 'size'
];
// 日付型として扱うカラム
protected $dates = ['deleted_at'];
// その他のモデル関連のメソッドやビジネスロジックをここに追加
}
uuidライブラリのインストール
LaravelでEloquentモデルの uuid フィールドを自動生成するには、モデルの作成イベントにフックしてUUIDを生成・割り当てる処理を追加します。これには、モデルのブートメソッドを使用します。以下に例を示します。
Composerを使って ramsey/uuid ライブラリをインストールします。
composer require ramsey/uuid
UUID生成処理を追加
次に、モデルクラス( Image )にUUIDを自動生成するロジックを追加します。
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str; // Strクラスをインポート
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str; // UUIDを使用する場合
class Image extends Model
{
use SoftDeletes;
protected $fillable = [
'uuid', 'name', 'path', 'size'
];
// ブートメソッドをオーバーライドして、UUIDを自動生成します。
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
// モデルが作成される前に、UUIDを生成して割り当てます。
if (empty($model->uuid)) {
$model->uuid = Str::uuid();
}
});
}
// その他のコード...
}
このコードでは、Image モデルが新しく作成されるときに、uuidカラムが空の場合にのみ新しいUUIDを生成し割り当てています。これにより、モデルを保存する際に自動的にUUIDが設定されます。
使用例
$image = new Image;
$image->name = 'example.jpg';
$image->path = '/path/to/image.jpg';
$image->size = 12345;
$image->save();
echo $image->uuid; // 自動生成されたUUID
この方法により、LaravelのEloquentモデルで uuid フィールドを自動的に生成・割り当てることができます。
画像アップロードの実装
レイアウトテンプレートの作成
まず、共通のレイアウトテンプレートを作成します。これは他のBladeビューで再利用するための基本的なHTML構造を提供します。デフォルトのレイアウトテンプレートを使用する場合は、下記は不要です。デフォルトのレイアウトテンプレートは、JetStreamで使用していますので、ここは別途作成したいと思います。
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title')</title>
@vite('resources/css/app.css') {{-- ViteによるCSSの読み込み --}}
</head>
<body class="bg-gray-100">
<div class="container mx-auto px-4">
@yield('content')
</div>
@vite('resources/js/app.js') {{-- ViteによるJavaScriptの読み込み --}}
</body>
</html>
画像アップロードビューの作成
次に、実際の画像アップロードフォームを含むビューファイルを作成します。
@extends('layouts.base')
@section('title', '画像アップロード')
@section('content')
<div class="max-w-md mx-auto my-10 bg-white p-5 rounded-md shadow-sm">
<div class="text-center">
<h1 class="my-3 text-3xl font-semibold text-gray-700">画像アップロード</h1>
<p class="text-gray-400">アップロードする画像を選択してください</p>
</div>
<div class="m-7">
<form action="/upload-image" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-6">
<label for="image" class="block mb-2 text-sm text-gray-600">画像</label>
<input type="file" name="image" id="image" class="w-full px-3 py-2 text-gray-700 bg-white border border-gray-300 rounded-md focus:outline-none" required>
</div>
<div class="mb-6">
<button type="submit" class="w-full px-3 py-4 text-white bg-blue-500 rounded-md focus:bg-blue-600 focus:outline-none">アップロード</button>
</div>
</form>
</div>
</div>
@endsection
ルートの設定
Laravelのルーティングで、画像アップロードページにアクセスできるように設定します。
use Illuminate\Support\Facades\Route;
Route::get('/image-upload', function () {
return view('image-upload');
});
画像をWebPに変換
Intervention Imageライブラリのインストール
ここでは、画像をWebPに変換し、異なるサイズで保存する処理を追加する必要があります。intervention/image ライブラリを使用すると、このような処理を簡単に行うことができます。
composerを利用してIntervention Imageをインストールします。
composer require intervention/image
例えば、元の画像をWebPに変換し、サイズを変更して保存するには以下のようにします
サービスプロバイダーの登録
Laravel 5.5以降ではサービスプロバイダーが自動的に登録されます。5.4以前を使用している場合、config/app.phpに手動で追加する必要があります。
'providers' => [
// その他のサービスプロバイダ
Intervention\Image\ImageServiceProvider::class
],
'aliases' => [
// その他のクラスエイリアス
'Image' => Intervention\Image\Facades\Image::class
],
基本的な使い方
- 画像の読み込みと変更
-
画像を読み込んで、リサイズ、回転などの操作を行います。
use Intervention\Image\Facades\Image; // ファイルパスから画像を読み込む $image = Image::make(storage_path('path/to/image.jpg')); // 画像をリサイズ $image->resize(300, 200); // 画像を回転 $image->rotate(45);
- 画像の保存
-
加工した画像をファイルとして保存します。
$image->save(storage_path('path/to/new_image.jpg'));
- 画像の出力
-
ブラウザに画像を直接出力することも可能です。
return $image->response('jpg');
- フォーマットの変更
-
画像のフォーマットを変更して保存することができます。
// 例: WebPフォーマットで保存 $image->encode('webp', 90)->save(storage_path('path/to/image.webp'));
- 注意点
-
- Intervention ImageはGDライブラリまたはImagick PHP拡張に依存しています。サーバーにこれらのいずれか(または両方)がインストールされていることを確認してください。
- サーバーのリソース制限(メモリなど)に注意しながら大きな画像を処理するようにしてください。必要に応じてPHPのメモリ制限を調整することが必要です。
画像変換と追加の処理
元の画像をWebPに変換し、サイズを変更して保存するには以下のようにします。
use Intervention\Image\Facades\Image as ImageIntervention;
// ...
$image = ImageIntervention::make($file)->encode('webp', 90); // 90は品質
// 元のサイズで保存
$image->save(storage_path('app/public/images/' . $filename . '.webp'));
// 縦横半分のサイズで保存
$image->resize($image->width() / 2, $image->height() / 2)->save(storage_path('app/public/images/' . $filename . '_half.webp'));
// 200x200で中央をクロップして保存
$image->fit(200, 200)->save(storage_path('app/public/images/' . $filename . '_thumb.webp'));
画像情報のデータベースへの保存
コントローラの生成
まず、画像アップロード用のコントローラを生成します。以下のコマンドをターミナルで実行してください。
php artisan make:controller ImageUploadController
このコマンドは、app/Http/Controllers ディレクトリに ImageUploadController.php という新しいコントローラファイルを生成します。
コントローラの編集
ImageUploadController.php を開き、画像アップロードのロジックを実装します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Image; // 事前に作成したImageモデル
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image as ImageIntervention;
class ImageUploadController extends Controller
{
public function upload(Request $request)
{
$request->validate([
'image' => 'required|image|max:2048', // 画像ファイルのバリデーション
]);
$file = $request->file('image');
$originalName = $file->getClientOriginalName();
$filename = time() . '_' . $originalName;
// $size = $file->getSize();
// 画像の保存処理
$oldPath = $file->storeAs('images/old', $filename, 'public');
// ここで画像をWebPに変換し、サイズに合わせて保存
$image = ImageIntervention::make($file)->encode('webp', 80); // 80は品質
// 元のサイズで保存
// $originalNameの.pngを削除する
$filename = str_replace('.png', '', $originalName);
$image->save(storage_path('app/public/images/' . $filename . '.webp'));
$newName = $filename . '.webp';
$newPath = 'images/' . $newName;
$size = $image->filesize();
// 縦横半分のサイズで保存
$image->resize($image->width() / 2, $image->height() / 2)->save(storage_path('app/public/images/' . $filename . '_half.webp'));
// 200x200で中央をクロップして保存
$image->fit(200, 200)->save(storage_path('app/public/images/' . $filename . '_thumb.webp'));
// データベースに画像情報を保存
$image = new Image;
$image->uuid = Str::uuid();
$image->name = $newName;
$image->path = $newPath;
$image->size = $size;
$image->save();
return $this->show($image->id)->with('success', '画像がアップロードされました。');
}
// 画像の表示
public function show($id)
{
$image = Image::findOrFail($id);
return view('image-show', ['image' => $image]);
}
}
ルーターの作成
ImageUploadController の upload メソッドに対するルーティングを作成します。
use App\Http\Controllers\ImageUploadController;
// 画像アップロード用のルート
Route::post('/upload-image', [ImageUploadController::class, 'upload']);
画像の表示
画像のパスの取得
Bladeテンプレートで画像を表示するには、<img>タグの src 属性に保存した画像のパスをデータベースから取得します。
画像の表示
例えば、Image モデルから取得した画像データを表示するには、以下のようにします。
<img src="{{ asset($image->path) }}" alt="画像の説明">
showメソッドの作成
まず、ImageUploadController に画像を表示するための show メソッドを追加します。このメソッドでは、特定の画像のデータをデータベースから取得し、それをビューに渡します。
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Image;
class ImageUploadController extends Controller
{
// その他のメソッド...
public function show($id)
{
$image = Image::findOrFail($id);
return view('image-show', ['image' => $image]);
}
}
このメソッドでは、指定されたIDを持つ画像を Image モデルを使用して検索し、見つかった画像オブジェクトを imge-show ビューに渡しています。findOrFail メソッドは、指定されたIDの画像が存在しない場合に404エラーを返します。
ビューファイルの作成
次に、画像を表示するためのBladeビューファイル image-show.blade.php を作成します。
@extends('layouts.base')
@section('title', '画像詳細')
@section('content')
<div class="max-w-md mx-auto bg-white p-5 rounded-md shadow-sm">
<div class="text-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">画像詳細</h1>
</div>
<img src="{{ asset('storage/' . $image->path) }}" alt="{{ $image->name }}" class="w-full h-auto">
<p class="text-center text-gray-600 mt-4">{{ $image->name }}</p>
</div>
@endsection
このビューファイルでは、@extends ディレクティブを使って base.blade.php のレイアウトを継承し、@section(‘content’) で画像の詳細を表示しています。@section(‘title’,’画像詳細’) を使用してページのタイトルも指定しています。
ルートの設定
最後に、この show メソッドにアクセスするためのルートを設定します。
use App\Http\Controllers\ImageUploadController;
Route::get('/images/{id}', [ImageUploadController::class, 'show']);
GDライブラリ
GD Library extension not available with this PHP installation.
このエラーメッセージは、PHPのGDライブラリがインストールまたは有効化されていないことを示しています。GDライブラリは、PHPで画像処理を行う際に必要な拡張機能です。このエラーを解決するには、GDライブラリをインストールし、PHP設定で有効にする必要があります。
GDライブラリのインストール
- Linux (Ubuntu/Debian) の場合
-
以下のコマンドを実行して、GDライブラリをインストールします
sudo apt update sudo apt -y upgrade sudo apt -y install php-gd
- Windowsの場合
-
- php.ini ファイルを開きます(通常はPHPのインストールディレクトリ内にあります)。
- ;extension=gd という行を探し、コメントアウト(先頭のセミコロン ; を削除)して有効化します。
- 変更を保存して、Webサーバー(Apache, Nginx, など)を再起動します。
- macOS の場合
-
Homebrewを使用している場合は、以下のコマンドでGDライブラリをインストールできます。
brew install php@8.2-gd
ここで、php@8.2-gd はインストールしているPHPのバージョンに応じて変更してください。
PHPの再起動
GDライブラリをインストールした後、変更を有効にするためにPHPを再起動する必要があります。Webサーバー(Apache, Nginxなど)を使用している場合は、そのサーバーを再起動してください。
例えば、Apacheを使用している場合は以下のコマンドを実行します。
sudo service apache2 restart
Nginxを利用している場合は、以下のコマンドを実行します。
sudo systemctl restart nginx
これで、GDライブラリがインストールされ有効化され、Laravelアプリケーションで画像処理が行えるようになります。エラーが解消されていることを確認するために、Laravelアプリケーションを再度実行してください。
保存先の変更
保存先の変更
現状は、Laravelのstorage/app/publicに画像データは保存されています。これを当初作成した、/var/www/storage/imagesディレクトリに保存する為に下記の様に修正します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Image; // 事前に作成したImageモデル
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image as ImageIntervention;
class ImageUploadController extends Controller
{
public function upload(Request $request)
{
$request->validate([
'image' => 'required|image|max:2048', // 画像ファイルのバリデーション
]);
$file = $request->file('image');
$originalName = $file->getClientOriginalName();
// 画像の保存先パスを定義
$image_path = '/var/www/storage/images';
$old_path = '/var/www/storage/images/old';
// 画像の保存処理
$oldImagePath = $old_path . '/' . $originalName;
$file->move($old_path, $originalName);
// Intervention Imageを使用して画像を処理
$image = ImageIntervention::make($oldImagePath)->encode('webp', 80);
// 元のサイズで保存
$filename = str_replace('.png', '', $originalName);
$image->save($image_path . '/' . $filename . '.webp');
$newName = $filename . '.webp';
$newPath = 'images/' . $newName;
$size = $image->filesize();
// 縦横半分のサイズで保存
$image->resize($image->width() / 2, $image->height() / 2)->save($image_path . '/' . $filename . '_half.webp');
// 200x200で中央をクロップして保存
$image->fit(200, 200)->save($image_path . '/' . $filename . '_thumb.webp');
// データベースに画像情報を保存
$image = new Image;
$image->uuid = Str::uuid();
$image->name = $newName;
$image->path = $newPath;
$image->size = $size;
$image->save();
return $this->show($image->id)->with('success', '画像がアップロードされました。');
}
// 画像の表示
public function show($id)
{
$image = Image::findOrFail($id);
return view('image-show', ['image' => $image]);
}
}
シンボリックリンクの作成
Laravelから/var/www/imagesへのシンボリックリンクを作成します。
ln -s /var/www/storage/images /path/to/laravel-project/public
Laravelからimagesディレクトリへアクセスできるようにオーナー変更します。
sudo chown -R www-data:www-data /var/www
ビューファイルの修正
@extends('layouts.base')
@section('title', '画像詳細')
@section('content')
<div class="max-w-md mx-auto bg-white p-5 rounded-md shadow-sm">
<div class="text-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">画像詳細</h1>
</div>
<img src="{{ asset('storage/'.$image->path) }}" alt="{{ $image->name }}" class="w-full h-auto">
<p class="text-center text-gray-600 mt-4">{{ $image->name }}</p>
</div>
<!-- セッションメッセージの表示 -->
@if (session('success'))
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 mt-4" role="alert">
<p>{{ session('success') }}</p>
</div>
@endif
@endsection
セッションメッセージを追加しました。
Storageファサードの利用
Laravelで画像を保存する際に、ストレージパスをカスタマイズして Storage ファサードを使う方法を説明します。ここでは、/var/www/storage/images と /var/www/storage/images/old に画像を保存するように設定します。
ストレージディスクの設定
まず、config/filesystems.php にカスタムストレージディスクを設定します。
'disks' => [
// その他のディスク設定...
'custom_images' => [
'driver' => 'local',
'root' => '/var/www/storage/images',
'url' => env('APP_URL').'/images',
'visibility' => 'public',
],
'old_images' => [
'driver' => 'local',
'root' => '/var/www/storage/images/old',
'visibility' => 'public',
],
],
ImageUploadControllerの修正
次に、ImageUploasController で Storage ファサードを使用して画像を保存します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Image; // 事前に作成したImageモデル
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image as ImageIntervention;
use Illuminate\Support\Facades\Storage;
class ImageUploadController extends Controller
{
public function upload(Request $request)
{
$request->validate([
'image' => 'required|image|max:2048', // 画像ファイルのバリデーション
]);
$file = $request->file('image');
$originalName = $file->getClientOriginalName();
// 古い画像を保存
$oldImagePath = Storage::disk('old_images')->putFileAs('', $file, $originalName);
// Intervention Imageを使用して画像を処理
$image = ImageIntervention::make($file)->encode('webp', 80);
// 元のサイズでWebP保存
$filename = str_replace('.png', '', $originalName);
$timestamp = time(); // タイムスタンプの取得
$newName = $timestamp . '_' . $filename . '.webp'; // タイムスタンプをファイル名に付与
Storage::disk('custom_images')->put($newName, (string) $image);
$newPath = 'images/';
$size = $image->filesize();
// 縦横半分のサイズでWebP保存
$image->resize($image->width() / 2, $image->height() / 2)->save($newPath . '/' . $timestamp . $filename . '_half.webp');
// 200x200で中央をクロップしてWebP保存
$image->fit(200, 200)->save($newPath . '/' . $timestamp . $filename . '_thumb.webp');
// データベースに画像情報を保存
$image = new Image;
$image->uuid = Str::uuid();
$image->name = $filename;
$image->path = $newPath . $newName;
$image->size = $size;
$image->save();
// return $this->show($image->id);
return back()->with('success', '画像がアップロードされました。');
}
// 画像の表示
public function show($id)
{
$image = Image::findOrFail($id);
return view('image-show', ['image' => $image]);
}
}
このコードでは、Storage::disk(‘custom_images’)->put( …) と Storage::disk(‘old_images’)->putFilesAs(…) を使用して、それぞれカスタマイズしたストレージディスクに画像を保存しています。
コメント