株式会社WR

株式会社WR

WEB TOTAL CONSULTING

Laravelでファイルアップロードを実装——バリデーション・保存・URLの生成
ブログ一覧へ
技術ブログ

Laravelでファイルアップロードを実装——バリデーション・保存・URLの生成

顧客管理システムやECサイトでよく使うファイルアップロード機能をLaravelで実装します。画像リサイズ・ファイル名の一意化・S3への保存方法も紹介します。

Laravelのファイルアップロード——全体の流れ

ファイルアップロードの実装は以下の流れで行います。

  1. フロントエンド(HTMLフォーム)でファイルを選択して送信
  2. LaravelのFormRequestでバリデーション
  3. Storage ファサードで保存
  4. DBにパスを記録
  5. URLを生成して返す

HTMLフォームの設定

<!-- enctype="multipart/form-data" が必須 -->
<form action="/upload" method="POST" enctype="multipart/form-data">
    @csrf
    <input type="file"
           name="avatar"
           accept="image/jpeg,image/png,image/webp"
           class="block w-full text-sm text-gray-600
                  file:mr-4 file:py-2 file:px-4 file:rounded-full
                  file:border-0 file:text-sm file:font-medium
                  file:bg-amber-50 file:text-amber-700
                  hover:file:bg-amber-100">
    <button type="submit" class="mt-4 px-6 py-2 bg-amber-700 text-white rounded-full">
        アップロード
    </button>
</form>

バリデーション

class UploadAvatarRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'avatar' => [
                'required',
                'file',
                'image',                 // MIME: jpeg, png, gif, bmp, webp, svg
                'mimes:jpeg,png,webp',   // 許可する拡張子
                'max:5120',              // 5MB以内(KBで指定)
                'dimensions:min_width=100,min_height=100,max_width=2000,max_height=2000',
            ],
        ];
    }

    public function messages(): array
    {
        return [
            'avatar.required'   => 'ファイルを選択してください',
            'avatar.image'      => '画像ファイルを選択してください',
            'avatar.mimes'      => 'JPEG・PNG・WebP形式のみ対応しています',
            'avatar.max'        => 'ファイルサイズは5MB以内にしてください',
            'avatar.dimensions' => '画像サイズは100px〜2000px以内にしてください',
        ];
    }
}

コントローラーでの保存

class UserAvatarController extends Controller
{
    public function store(UploadAvatarRequest $request): JsonResponse
    {
        $user = $request->user();

        // 既存のアバターを削除
        if ($user->avatar_path) {
            Storage::disk('public')->delete($user->avatar_path);
        }

        // ファイルを保存(ユニークなファイル名を自動生成)
        $path = $request->file('avatar')
                        ->store("avatars/{$user->id}", 'public');
        // → storage/app/public/avatars/1/xxxxxxxx.jpg

        // DBに保存
        $user->update(['avatar_path' => $path]);

        // 公開URLを返す
        $url = Storage::disk('public')->url($path);

        return response()->json([
            'message' => 'アバターを更新しました',
            'url'     => $url,
        ]);
    }
}

ファイル名のカスタマイズ

use Illuminate\Support\Str;

// カスタムファイル名で保存
$extension = $request->file('avatar')->extension();
$filename  = Str::uuid() . '.' . $extension;

$path = $request->file('avatar')
                ->storeAs("avatars/{$user->id}", $filename, 'public');

画像のリサイズ処理

アップロード後にリサイズすることで、ストレージの節約とページ表示速度の向上が図れます。

composer require intervention/image
use Intervention\Image\Facades\Image;

class UserAvatarController extends Controller
{
    public function store(UploadAvatarRequest $request): JsonResponse
    {
        $file = $request->file('avatar');

        // リサイズ処理
        $image = Image::make($file->getRealPath())
                      ->fit(400, 400)           // 400x400にクロップ
                      ->encode('webp', 85);      // WebPに変換(品質85%)

        $filename = Str::uuid() . '.webp';
        $path     = "avatars/{$request->user()->id}/{$filename}";

        Storage::disk('public')->put($path, $image->getEncoded());

        $request->user()->update(['avatar_path' => $path]);

        return response()->json([
            'url' => Storage::disk('public')->url($path),
        ]);
    }
}

複数ファイルのアップロード

// バリデーション
'photos'   => ['required', 'array', 'max:10'],
'photos.*' => ['image', 'mimes:jpeg,png,webp', 'max:5120'],

// 保存
$paths = [];
foreach ($request->file('photos') as $photo) {
    $paths[] = $photo->store("listings/{$listingId}/photos", 'public');
}

// DBに一括保存
$listingPhotos = array_map(fn($path) => [
    'listing_id' => $listingId,
    'path'       => $path,
    'url'        => Storage::disk('public')->url($path),
    'created_at' => now(),
    'updated_at' => now(),
], $paths);

DB::table('listing_photos')->insert($listingPhotos);

S3へのアップロード

本番環境ではAmazon S3やCloudflare R2などのオブジェクトストレージを使うことを推奨します。

composer require league/flysystem-aws-s3-v3
// .env
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=my-bucket

// アップロード
$path = $request->file('avatar')->store("avatars/{$user->id}", 's3');
$url  = Storage::disk('s3')->url($path);

まとめ

Laravelのファイルアップロードは、FormRequestによるバリデーション・Storageファサードによる保存・URL生成という3ステップで実装できます。画像リサイズ・S3連携を加えることで本番運用に耐える実装が完成します。弊社では物件画像・プロフィール写真など、様々なファイルアップロード機能を実装してきました。

LaravelによるWebシステム開発のご相談はお気軽にどうぞ。

Category 技術ブログ

Related Posts

関連記事

開発・技術のご相談はお気軽に

お見積りは無料です。まずはお気軽にご相談ください。

お問い合わせ →