Laravel Storageとは:基礎概念を理解する
Laravel Storageは、Laravelフレームワークに組み込まれたファイルシステム抽象化レイヤーです。このコンポーネントにより、ローカルファイルシステムやクラウドストレージサービスを統一的なインターフェースで操作することが可能になります。
ファイルストレージシステムの全体像
Laravel Storageは、Flysystemというライブラリをベースに構築されています。以下が主要なコンポーネントと特徴です:
- ストレージドライバー
- ローカルドライバー:サーバー上のファイルシステムを操作
- AWSドライバー:Amazon S3との連携
- FTPドライバー:FTPサーバーとの連携
- その他:Google Cloud Storage、Azure Blobなど
- ディスク(Disk)の概念
- 物理的なストレージの抽象化
- 設定ファイルで簡単に切り替え可能
- 複数のディスクを同時に利用可能
- 統一されたAPI
// 同じメソッドで異なるストレージを操作
Storage::disk('local')->put('file.txt', 'Content');
Storage::disk('s3')->put('file.txt', 'Content');
Laravel Storageが解決する3つの課題
- ストレージの依存性問題
- 従来:特定のストレージに依存したコードが必要
- 解決策:抽象化レイヤーによる統一的なインターフェース
- メリット:ストレージの切り替えが容易に
- セキュリティリスク
- 従来:直接的なファイルパス操作によるリスク
- 解決策:安全なファイルパス生成と検証
- メリット:セキュリティホールの防止
- 実装の複雑さ
- 従来:各ストレージごとに異なる実装が必要
- 解決策:シンプルで一貫性のあるAPI
- メリット:開発効率の向上
従来のPHPファイル処理との比較
1. 基本的なファイル操作の比較
従来のPHP:
// ファイル書き込み
$content = 'Hello World';
file_put_contents('/path/to/file.txt', $content);
// ファイル読み込み
$content = file_get_contents('/path/to/file.txt');
Laravel Storage:
// ファイル書き込み
Storage::put('file.txt', 'Hello World');
// ファイル読み込み
$content = Storage::get('file.txt');
2. 主要な違いと利点
| 機能 | 従来のPHP | Laravel Storage |
|---|---|---|
| パス指定 | 絶対/相対パスが必要 | 論理パスで指定可能 |
| エラーハンドリング | try-catchが必須 | 統一的な例外処理 |
| ストレージ切替 | コード変更が必要 | 設定変更のみ |
| セキュリティ | 自前で実装が必要 | 標準で対応済み |
3. 実装の柔軟性
Laravel Storageでは、以下のような高度な操作も簡単に実装できます:
// ファイルの存在確認
if (Storage::exists('file.txt')) {
// 処理
}
// ファイルのURL取得
$url = Storage::url('file.txt');
// ファイルのメタデータ取得
$size = Storage::size('file.txt');
$lastModified = Storage::lastModified('file.txt');
このように、Laravel Storageは従来のPHPファイル処理の課題を解決し、より安全で効率的なファイル操作を実現します。次のセクションでは、実際の環境構築と基本設定について詳しく説明していきます。
Laravel Storageの環境構築と基本設定
Laravel Storageを効果的に活用するためには、適切な環境構築と設定が不可欠です。このセクションでは、具体的な設定方法とベストプラクティスを解説します。
設定ファイルconfig/filesystems.phpの詳細解説
Laravel Storageの設定は、config/filesystems.phpで管理されています。主要な設定項目は以下の通りです:
- デフォルトディスクの設定
'default' => env('FILESYSTEM_DISK', 'local'),
- ディスクの設定
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
],
'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'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
],
- シンボリックリンクの設定
'links' => [
public_path('storage') => storage_path('app/public'),
],
ローカルディスクとクラウドストレージの設定方法
1. ローカルディスクの設定
基本的なローカルディスクのセットアップ手順:
// config/filesystems.php
'disks' => [
'custom' => [
'driver' => 'local',
'root' => storage_path('app/custom'),
'permissions' => [
'file' => [
'public' => 0664,
'private' => 0600,
],
'dir' => [
'public' => 0775,
'private' => 0700,
],
],
],
],
2. クラウドストレージの設定
Amazon S3の設定例:
- 必要パッケージのインストール
composer require league/flysystem-aws-s3-v3
- 環境変数の設定(.env)
AWS_ACCESS_KEY_ID=your-key-id AWS_SECRET_ACCESS_KEY=your-secret AWS_DEFAULT_REGION=your-region AWS_BUCKET=your-bucket
- ディスク設定の確認
// config/filesystems.php
's3' => [
'driver' => 's3',
// ... 他の設定
],
ストレージドライバーの選択とセットアップ
1. 利用可能なドライバー
| ドライバー | 用途 | 必要パッケージ |
|---|---|---|
| local | ローカルファイルシステム | デフォルト搭載 |
| ftp | FTPサーバー | league/flysystem-ftp |
| sftp | SFTPサーバー | league/flysystem-sftp-v3 |
| s3 | Amazon S3 | league/flysystem-aws-s3-v3 |
| gcs | Google Cloud Storage | google/cloud-storage |
2. ドライバー選択の基準
- 開発環境:
localドライバーが推奨 - 本番環境:用途に応じて
s3やgcsを選択 - セキュリティ要件:
sftpの使用を検討 - レガシーシステム連携:
ftpドライバーを活用
3. カスタムドライバーの実装
独自のストレージドライバーが必要な場合は、以下のように実装できます:
use League\Flysystem\FilesystemAdapter;
use Storage;
class CustomAdapter implements FilesystemAdapter
{
// アダプターの実装
}
// サービスプロバイダーでの登録
Storage::extend('custom', function ($app, $config) {
return new CustomAdapter($config);
});
以上で基本的な環境構築と設定の解説を終わります。次のセクションでは、実践的なファイル操作テクニックについて説明していきます。
実践的なファイル操作テクニック
Laravel Storageを使用した実践的なファイル操作について、具体的な実装方法とベストプラクティスを解説します。
ファイルのアップロード処理の実装方法
1. 基本的なファイルアップロード
public function store(Request $request)
{
if ($request->hasFile('file')) {
// ファイル名を元のファイル名で保存
$path = $request->file('file')->store('uploads');
// カスタムファイル名で保存
$customPath = $request->file('file')->storeAs(
'uploads',
time() . '_' . $request->file('file')->getClientOriginalName()
);
}
}
2. 画像アップロードの実装例
public function uploadImage(Request $request)
{
$request->validate([
'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048'
]);
try {
$image = $request->file('image');
$name = time() . '.' . $image->getClientOriginalExtension();
// 公開ディレクトリに保存
$path = Storage::disk('public')->putFileAs(
'images',
$image,
$name
);
return response()->json([
'success' => true,
'path' => Storage::disk('public')->url($path)
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage()
], 500);
}
}
3. 大容量ファイルのチャンクアップロード
public function uploadChunk(Request $request)
{
$chunkNumber = $request->input('chunk_number');
$totalChunks = $request->input('total_chunks');
$fileId = $request->input('file_id');
// チャンクの一時保存
$chunk = $request->file('chunk');
$chunkPath = "chunks/{$fileId}/{$chunkNumber}";
Storage::put($chunkPath, file_get_contents($chunk));
// 全チャンクが揃ったら結合
if ($this->allChunksUploaded($fileId, $totalChunks)) {
$this->mergeChunks($fileId, $totalChunks);
}
}
private function mergeChunks($fileId, $totalChunks)
{
$finalPath = "uploads/{$fileId}";
$buffer = '';
for ($i = 0; $i < $totalChunks; $i++) {
$chunkPath = "chunks/{$fileId}/{$i}";
$buffer .= Storage::get($chunkPath);
Storage::delete($chunkPath);
}
Storage::put($finalPath, $buffer);
}
セキュアなファイルダウンロードの実現方法
1. 認証付きダウンロード
public function download($fileId)
{
// ファイルの存在確認と権限チェック
$file = File::findOrFail($fileId);
$this->authorize('download', $file);
if (!Storage::exists($file->path)) {
abort(404);
}
// ダウンロード回数の記録などの付加的な処理
$file->increment('download_count');
// セキュアなダウンロードの実行
return Storage::download(
$file->path,
$file->original_name,
['Content-Type' => $file->mime_type]
);
}
2. 一時的なダウンロードURL生成
public function getTemporaryUrl($fileId)
{
$file = File::findOrFail($fileId);
// S3の場合の一時URL生成
if (Storage::disk('s3')->exists($file->path)) {
return Storage::disk('s3')->temporaryUrl(
$file->path,
now()->addMinutes(5),
[
'ResponseContentDisposition' => 'attachment; filename="' . $file->original_name . '"'
]
);
}
// ローカルストレージの場合の署名付きURL生成
return URL::signedRoute(
'download',
['file' => $file->id],
now()->addMinutes(5)
);
}
一時ファイルの効率的な扱い方
1. 一時ファイルの作成と管理
class TemporaryFileManager
{
public function store(UploadedFile $file)
{
$path = $file->store('temp');
// 一時ファイル情報のデータベース記録
return TemporaryFile::create([
'path' => $path,
'original_name' => $file->getClientOriginalName(),
'expires_at' => now()->addHours(24)
]);
}
public function cleanup()
{
$expiredFiles = TemporaryFile::where('expires_at', '<', now())->get();
foreach ($expiredFiles as $file) {
Storage::delete($file->path);
$file->delete();
}
}
}
2. 一時ファイルを永続化
public function makePermanent(TemporaryFile $tempFile, $destination)
{
try {
// 一時ファイルを永続的な保存場所に移動
$newPath = Storage::move(
$tempFile->path,
$destination . '/' . $tempFile->original_name
);
// データベース更新
$permanentFile = PermanentFile::create([
'path' => $newPath,
'original_name' => $tempFile->original_name
]);
// 一時ファイル情報の削除
$tempFile->delete();
return $permanentFile;
} catch (\Exception $e) {
\Log::error('Failed to make file permanent: ' . $e->getMessage());
throw $e;
}
}
3. 定期的なクリーンアップの実装
// app/Console/Commands/CleanupTemporaryFiles.php
class CleanupTemporaryFiles extends Command
{
protected $signature = 'files:cleanup';
public function handle(TemporaryFileManager $manager)
{
$manager->cleanup();
}
}
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('files:cleanup')->daily();
}
これらの実装例は、実際の開発現場で使用できる実践的なコードです。次のセクションでは、パフォーマンスとセキュリティの最適化について説明していきます。
パフォーマンスとセキュリティの最適化
Laravel Storageを本番環境で効率的に運用するためには、パフォーマンスとセキュリティの両面での最適化が重要です。このセクションでは、実践的な最適化手法を解説します。
大容量ファイル処理のベストプラクティス
1. メモリ効率の良いストリーミング処理
// 非効率な方法(メモリに全データを保持)
$content = Storage::get('large-file.zip');
Storage::disk('s3')->put('backup/large-file.zip', $content);
// 効率的な方法(ストリーミング処理)
$stream = Storage::readStream('large-file.zip');
Storage::disk('s3')->writeStream('backup/large-file.zip', $stream);
if (is_resource($stream)) {
fclose($stream);
}
2. 大容量ファイルのチャンク処理
class LargeFileProcessor
{
private $chunkSize = 1024 * 1024; // 1MB単位で処理
public function process($filePath)
{
$stream = Storage::readStream($filePath);
while (!feof($stream)) {
$chunk = fread($stream, $this->chunkSize);
$this->processChunk($chunk);
// メモリ解放
if (gc_enabled()) {
gc_collect_cycles();
}
}
fclose($stream);
}
}
3. 非同期処理の活用
// ファイルアップロード処理をキューに投入
class ProcessLargeFile implements ShouldQueue
{
public function handle()
{
// タイムアウト時間の設定
set_time_limit(0);
// 進捗監視付きの処理
$manager = new FileProcessManager();
$manager->processWithProgress($this->filePath, function ($progress) {
Cache::put("file_progress_{$this->fileId}", $progress, 3600);
});
}
}
ストレージセキュリティの確保方法
1. アクセス制御の基本設定
// config/filesystems.php
return [
'disks' => [
'private' => [
'driver' => 'local',
'root' => storage_path('app/private'),
'visibility' => 'private',
'permissions' => [
'file' => [
'public' => 0644,
'private' => 0600,
],
'dir' => [
'public' => 0755,
'private' => 0700,
],
],
],
],
];
2. セキュアなファイルアクセス制御
class SecureFileController extends Controller
{
public function download($fileId)
{
$file = File::findOrFail($fileId);
// 権限チェック
if (!auth()->user()->can('view', $file)) {
abort(403);
}
// アクセスログの記録
$this->logFileAccess($file);
// 安全なダウンロード
return Storage::download(
$file->path,
$file->original_name,
[
'Content-Type' => $file->mime_type,
'Content-Disposition' => 'attachment'
]
);
}
}
3. ファイルアップロードのバリデーション
class FileUploadRequest extends FormRequest
{
public function rules()
{
return [
'file' => [
'required',
'file',
'mimes:jpeg,png,pdf,doc,docx',
'max:10240', // 10MB
function ($attribute, $value, $fail) {
if (!$this->validateFileContent($value)) {
$fail('ファイルの内容が不正です。');
}
},
]
];
}
private function validateFileContent($file)
{
// ウイルススキャンやファイル形式の検証
return true;
}
}
キャッシュを活用した読み書きの高速化
1. メタデータのキャッシュ
class FileMetadata
{
public function get($path)
{
$cacheKey = "file_meta_{$path}";
return Cache::remember($cacheKey, 3600, function () use ($path) {
return [
'size' => Storage::size($path),
'modified' => Storage::lastModified($path),
'mime' => Storage::mimeType($path),
];
});
}
}
2. 頻繁にアクセスされるファイルの最適化
class OptimizedFileService
{
public function serveFile($path)
{
// アクセス頻度の監視
$accessCount = Cache::increment("file_access_{$path}");
// 頻繁にアクセスされるファイルは自動的にキャッシュ
if ($accessCount > 100) {
return Cache::remember("file_content_{$path}", 3600, function () use ($path) {
return Storage::get($path);
});
}
return Storage::get($path);
}
}
3. 読み書きのパフォーマンス監視
class StoragePerformanceMonitor
{
public function monitor($operation, $path, callable $callback)
{
$startTime = microtime(true);
try {
$result = $callback();
// 処理時間の記録
$duration = microtime(true) - $startTime;
$this->logPerformance($operation, $path, $duration);
return $result;
} catch (\Exception $e) {
$this->logError($operation, $path, $e);
throw $e;
}
}
private function logPerformance($operation, $path, $duration)
{
Log::info("Storage {$operation} completed", [
'path' => $path,
'duration' => $duration,
'memory_usage' => memory_get_peak_usage(true)
]);
}
}
これらの最適化とセキュリティ対策を適切に組み合わせることで、安全で高速なファイルストレージシステムを実現できます。特に大規模なアプリケーションでは、これらの対策が重要になってきます。
クラウドストレージとの連携実装
Laravel Storageを使用したクラウドストレージとの連携方法について、具体的な実装手順と実践的なコード例を解説します。
AWS S3との連携設定と使い方
1. 初期設定
# 必要パッケージのインストール composer require league/flysystem-aws-s3-v3 # .env ファイルの設定 AWS_ACCESS_KEY_ID=your-key AWS_SECRET_ACCESS_KEY=your-secret AWS_DEFAULT_REGION=ap-northeast-1 AWS_BUCKET=your-bucket AWS_USE_PATH_STYLE_ENDPOINT=false
2. 基本的な使用方法
class S3StorageService
{
public function uploadToS3($file, $path)
{
try {
// パブリックアクセス可能なファイルのアップロード
$result = Storage::disk('s3')->putFileAs(
'public/uploads',
$file,
$path,
['visibility' => 'public']
);
if ($result) {
return Storage::disk('s3')->url($path);
}
return null;
} catch (\Exception $e) {
Log::error('S3 upload failed: ' . $e->getMessage());
throw $e;
}
}
public function getSignedUrl($path, $expiration = 5)
{
try {
// 署名付きURL生成(期限付きアクセス)
return Storage::disk('s3')->temporaryUrl(
$path,
now()->addMinutes($expiration),
[
'ResponseContentDisposition' => 'attachment'
]
);
} catch (\Exception $e) {
Log::error('Failed to generate signed URL: ' . $e->getMessage());
throw $e;
}
}
}
3. S3バケットポリシーの設定例
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/public/*"
}
]
}
Google Cloud Storageの活用方法
1. 環境設定
# 必要パッケージのインストール composer require google/cloud-storage # .env ファイルの設定 GOOGLE_CLOUD_PROJECT_ID=your-project-id GOOGLE_CLOUD_STORAGE_BUCKET=your-bucket GOOGLE_CLOUD_CREDENTIALS=path/to/credentials.json
2. サービスプロバイダーの設定
namespace App\Providers;
use Google\Cloud\Storage\StorageClient;
use League\Flysystem\Filesystem;
use League\Flysystem\GoogleCloudStorage\GoogleCloudStorageAdapter;
use Storage;
class GoogleCloudServiceProvider extends ServiceProvider
{
public function boot()
{
Storage::extend('gcs', function ($app, $config) {
$storageClient = new StorageClient([
'projectId' => $config['project_id'],
'keyFilePath' => $config['credentials_path']
]);
$bucket = $storageClient->bucket($config['bucket']);
$adapter = new GoogleCloudStorageAdapter($bucket);
return new Filesystem($adapter);
});
}
}
3. 実装例
class GCSStorageService
{
public function uploadToGCS($file, $path)
{
try {
// メタデータ付きでアップロード
return Storage::disk('gcs')->put(
$path,
file_get_contents($file),
[
'metadata' => [
'contentType' => $file->getMimeType(),
'cacheControl' => 'public, max-age=86400'
]
]
);
} catch (\Exception $e) {
Log::error('GCS upload failed: ' . $e->getMessage());
throw $e;
}
}
public function getPublicUrl($path)
{
return Storage::disk('gcs')->url($path);
}
}
マルチクラウド環境での運用テクニック
1. クラウドストレージ抽象化サービス
class CloudStorageService
{
private $defaultDisk;
public function __construct()
{
$this->defaultDisk = config('filesystems.default');
}
public function upload($file, $path, $disk = null)
{
$disk = $disk ?? $this->defaultDisk;
try {
$result = Storage::disk($disk)->putFileAs(
dirname($path),
$file,
basename($path)
);
return [
'success' => $result,
'path' => $path,
'disk' => $disk,
'url' => $this->getUrl($path, $disk)
];
} catch (\Exception $e) {
Log::error("Upload failed on disk {$disk}: " . $e->getMessage());
throw $e;
}
}
public function getUrl($path, $disk = null)
{
$disk = $disk ?? $this->defaultDisk;
switch ($disk) {
case 's3':
return Storage::disk($disk)->temporaryUrl(
$path,
now()->addMinutes(5)
);
case 'gcs':
return Storage::disk($disk)->url($path);
default:
return Storage::disk($disk)->url($path);
}
}
}
2. フェイルオーバー機能の実装
class FailoverStorageService
{
private $disks = ['s3', 'gcs', 'local'];
public function store($file, $path)
{
foreach ($this->disks as $disk) {
try {
$result = Storage::disk($disk)->putFileAs(
dirname($path),
$file,
basename($path)
);
if ($result) {
return [
'success' => true,
'disk' => $disk,
'path' => $path
];
}
} catch (\Exception $e) {
Log::warning("Storage failed on {$disk}: " . $e->getMessage());
continue;
}
}
throw new \Exception('All storage attempts failed');
}
public function retrieve($path)
{
foreach ($this->disks as $disk) {
try {
if (Storage::disk($disk)->exists($path)) {
return Storage::disk($disk)->get($path);
}
} catch (\Exception $e) {
continue;
}
}
throw new \Exception('File not found in any storage');
}
}
3. 分散ストレージの管理
class DistributedStorageManager
{
private $config = [
'images' => 's3',
'documents' => 'gcs',
'temp' => 'local'
];
public function store($file, $type)
{
if (!isset($this->config[$type])) {
throw new \InvalidArgumentException('Invalid file type');
}
$disk = $this->config[$type];
$path = $this->generatePath($file, $type);
return Storage::disk($disk)->putFileAs(
$type,
$file,
basename($path)
);
}
private function generatePath($file, $type)
{
$hash = md5(uniqid() . $file->getClientOriginalName());
return sprintf(
'%s/%s/%s.%s',
$type,
substr($hash, 0, 2),
$hash,
$file->getClientOriginalExtension()
);
}
}
これらの実装により、複数のクラウドストレージを効率的に管理し、フェイルオーバーや分散ストレージなどの高度な機能を実現できます。
トラブルシューティングとデバッグ
Laravel Storageを運用する中で発生する可能性のある問題とその解決方法、効果的なデバッグ手法について解説します。
よくある問題と解決方法
1. パーミッション関連の問題
// 問題: storage ディレクトリへの書き込み権限エラー
Permission denied (publicPath: /var/www/html/storage/app/public)
// 解決方法
class StoragePermissionsFixer
{
public function fix()
{
$paths = [
storage_path('app'),
storage_path('app/public'),
public_path('storage')
];
foreach ($paths as $path) {
// ディレクトリ存在確認
if (!file_exists($path)) {
mkdir($path, 0755, true);
}
// 権限設定
chmod($path, 0755);
chown($path, 'www-data');
}
// シンボリックリンクの再作成
if (file_exists(public_path('storage'))) {
unlink(public_path('storage'));
}
Artisan::call('storage:link');
}
}
2. ディスク設定の問題
// 問題: ディスクの設定が見つからないエラー
class DiskConfigurationChecker
{
public function validateConfig()
{
$disks = config('filesystems.disks');
$errors = [];
foreach ($disks as $name => $config) {
if (!isset($config['driver'])) {
$errors[] = "Disk {$name} is missing driver configuration";
}
if ($config['driver'] === 's3') {
$required = ['key', 'secret', 'region', 'bucket'];
foreach ($required as $field) {
if (empty($config[$field])) {
$errors[] = "S3 disk {$name} is missing {$field}";
}
}
}
}
return $errors;
}
public function fixCommonIssues()
{
// 設定ファイルのキャッシュクリア
Artisan::call('config:clear');
// .envファイルの存在確認
if (!file_exists(base_path('.env'))) {
copy(base_path('.env.example'), base_path('.env'));
Artisan::call('key:generate');
}
}
}
3. ファイルアップロードの問題
class FileUploadDebugger
{
public function debugUpload($file)
{
$issues = [];
// ファイルサイズの確認
if ($file->getSize() > ini_get('upload_max_filesize')) {
$issues[] = 'File exceeds PHP upload_max_filesize';
}
// MIMEタイプの確認
$mimeType = $file->getMimeType();
if (!in_array($mimeType, $this->getAllowedMimeTypes())) {
$issues[] = "Invalid MIME type: {$mimeType}";
}
// アップロード先ディレクトリの確認
$uploadPath = storage_path('app/uploads');
if (!is_writable($uploadPath)) {
$issues[] = "Upload directory is not writable: {$uploadPath}";
}
return $issues;
}
private function getAllowedMimeTypes()
{
return [
'image/jpeg',
'image/png',
'application/pdf',
// その他許可するMIMEタイプ
];
}
}
効果的なログ設定とモニタリング
1. カスタムログチャンネルの設定
// config/logging.php
'channels' => [
'storage' => [
'driver' => 'daily',
'path' => storage_path('logs/storage.log'),
'level' => 'debug',
'days' => 14,
],
],
// 使用例
class StorageLogger
{
private $logger;
public function __construct()
{
$this->logger = Log::channel('storage');
}
public function logOperation($operation, $path, $result)
{
$context = [
'operation' => $operation,
'path' => $path,
'disk' => config('filesystems.default'),
'user_id' => auth()->id(),
'ip' => request()->ip(),
'result' => $result
];
$this->logger->info('Storage operation completed', $context);
}
public function logError($operation, \Exception $e)
{
$this->logger->error('Storage operation failed', [
'operation' => $operation,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
}
}
2. パフォーマンスモニタリング
class StorageMonitor
{
public function recordMetrics($operation, callable $callback)
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
try {
$result = $callback();
$this->saveMetrics([
'operation' => $operation,
'duration' => microtime(true) - $startTime,
'memory_used' => memory_get_usage() - $startMemory,
'status' => 'success'
]);
return $result;
} catch (\Exception $e) {
$this->saveMetrics([
'operation' => $operation,
'duration' => microtime(true) - $startTime,
'memory_used' => memory_get_usage() - $startMemory,
'status' => 'error',
'error' => $e->getMessage()
]);
throw $e;
}
}
private function saveMetrics($metrics)
{
StorageMetric::create($metrics);
}
}
本番環境での注意点と対策
1. エラーハンドリング
class ProductionStorageService
{
public function safeOperation(callable $operation)
{
try {
return $operation();
} catch (\League\Flysystem\FileNotFoundException $e) {
Log::error('File not found', [
'message' => $e->getMessage(),
'path' => $e->getPath()
]);
throw new FileNotFoundException($e->getMessage());
} catch (\League\Flysystem\UnreadableFileException $e) {
Log::error('File unreadable', [
'message' => $e->getMessage()
]);
throw new StorageException('Unable to read file');
} catch (\Exception $e) {
Log::error('Unexpected storage error', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw new StorageException('Storage operation failed');
}
}
}
2. バックアップと復旧手順
class StorageBackupService
{
public function createBackup($disk = null)
{
$disk = $disk ?? config('filesystems.default');
$backupPath = 'backups/' . date('Y-m-d_H-i-s');
// 重要なファイルのバックアップ
$files = Storage::disk($disk)->allFiles('public');
foreach ($files as $file) {
try {
$content = Storage::disk($disk)->get($file);
Storage::disk('backup')->put(
$backupPath . '/' . $file,
$content
);
} catch (\Exception $e) {
Log::error("Backup failed for file: {$file}");
continue;
}
}
// バックアップメタデータの保存
$this->saveBackupMetadata($backupPath, $files);
}
private function saveBackupMetadata($backupPath, $files)
{
$metadata = [
'timestamp' => now(),
'file_count' => count($files),
'source_disk' => config('filesystems.default'),
'backup_path' => $backupPath
];
Storage::disk('backup')->put(
$backupPath . '/metadata.json',
json_encode($metadata, JSON_PRETTY_PRINT)
);
}
}
これらのトラブルシューティングと監視の実装により、Laravel Storageを本番環境で安定して運用することができます。また、問題が発生した際の早期発見と迅速な対応が可能になります。