x-laravel/spatie-media-library-uuid-path

A UUID-based path generator for spatie/laravel-medialibrary.

Maintainers

Package info

github.com/x-laravel/spatie-media-library-uuid-path

pkg:composer/x-laravel/spatie-media-library-uuid-path

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-04-22 12:37 UTC

This package is auto-updated.

Last update: 2026-04-22 12:40:55 UTC


README

Tests PHP Laravel License

A UUID-based path generator for spatie/laravel-medialibrary.

The Problem

spatie/laravel-medialibrary's default DefaultPathGenerator stores media files in a flat structure based on the primary key (ID):

1/photo.jpg
2/photo.jpg
3/photo.jpg
...

This works fine for small applications, but causes serious issues as the number of media files grows:

  • File system performance degradation: File systems like ext4 and NTFS slow down directory listings when thousands of subdirectories exist under a single parent.
  • Predictable URLs: The sequential ID-based structure makes media file URLs trivially easy to enumerate.
  • Operational overhead: Bulk-moving, backing up, or migrating files to a CDN becomes harder with a flat layout.

The Solution

This package distributes files evenly by turning the first 8 characters of each media UUID into a four-level directory hierarchy:

55/0e/84/00/550e8400-e29b-41d4-a716-446655440000/photo.jpg

Conversions and responsive images are placed in dedicated subdirectories under the UUID folder:

55/0e/84/00/550e8400-e29b-41d4-a716-446655440000/conversions/
55/0e/84/00/550e8400-e29b-41d4-a716-446655440000/responsive-images/

Benefits:

  • Performance: Each directory holds at most 256 subdirectories, spreading the file system load evenly.
  • Security: The UUID-based random structure makes file paths unpredictable and resistant to enumeration.
  • Uniqueness: Every media file gets its own UUID directory, eliminating any risk of path collisions.

Requirements

  • PHP ^8.2
  • spatie/laravel-medialibrary ^11.0

Installation

composer require x-laravel/spatie-media-library-uuid-path

Setup

Publish the spatie/laravel-medialibrary config (if you haven't already) and set the path_generator option:

php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="config"

In config/media-library.php:

'path_generator' => \XLaravel\SpatieMediaLibraryUuidPath\UuidPathGenerator::class,

'file_remover_class' => \XLaravel\SpatieMediaLibraryUuidPath\UuidFileRemover::class,

Why UuidFileRemover? The default file remover deletes the UUID directory but leaves the empty shard parent directories (55/0e/84/00/) behind. UuidFileRemover cascades upward and removes each shard level when it becomes empty.

Migrating from DefaultPathGenerator

If your project already has media files stored with spatie's default ID-based structure (1/photo.jpg, 2/photo.jpg), you can migrate them to the UUID path structure without any downtime:

1. While your config still points to DefaultPathGenerator, run:

php artisan media:migrate-to-uuid

2. Once it completes, switch the config to this package:

'path_generator' => \XLaravel\SpatieMediaLibraryUuidPath\UuidPathGenerator::class,
'file_remover_class' => \XLaravel\SpatieMediaLibraryUuidPath\UuidFileRemover::class,

The command moves all files (including conversions and responsive images), deletes the old ID directories, and is safe to re-run — it skips media whose old directory no longer exists.

Options:

Option Description
disk Disk to migrate (defaults to media-library.disk_name config)
--dry-run Preview what would be moved without touching any files
--force Skip the production confirmation prompt

Cleaning Orphaned Directories

spatie's built-in media-library:clean command identifies orphaned directories using an is_numeric() check, which only works for the default ID-based path structure. This package ships a UUID-aware replacement:

php artisan media:clean-uuid

Options:

Option Description
disk Disk to clean (defaults to media-library.disk_name config)
--dry-run List orphaned directories without deleting them
--force Skip the production confirmation prompt

Running Tests

# Locally
vendor/bin/phpunit

# Via Docker (specific PHP version)
docker compose --profile php82 run --rm php82
docker compose --profile php83 run --rm php83
docker compose --profile php84 run --rm php84
docker compose --profile php85 run --rm php85