jegex/laravel-media
最新稳定版本:v1.0.0-alpha.5
Composer 安装命令:
composer require jegex/laravel-media
包简介
Laravel media library — upload, image conversions, responsive images, video thumbnails, ZIP export, and more
README 文档
README
Powerful media management package for Laravel applications. Handle file uploads, image conversions, responsive images, and video thumbnails with ease. Inspired by spatie/laravel-medialibrary.
Table of Contents
- Installation
- Quick Start
- Usage
- Image Conversions
- Responsive Images
- Queue System
- Vapor Uploads
- ZIP Export
- Facade Usage
- Configuration
- Environment Variables
- Advanced Usage
- Testing
- Changelog
- License
Installation
Install the package via Composer:
composer require jegex/laravel-media
Post-Installation Steps
Step 1: Publish Configuration & Migration
# Publish config file to config/media.php php artisan vendor:publish --tag="media-config" # Publish migration file php artisan vendor:publish --tag="media-migrations"
Step 2: Run Migration
php artisan migrate
Step 4: Configure Storage Disk (Optional)
In config/media.php or your .env file, set the default storage disk:
MEDIA_DISK=public
Then create the symbolic link for public disk:
php artisan storage:link
Step 5: Configure Queue (Optional)
If you want image conversions to run asynchronously:
QUEUE_CONNECTION=redis QUEUE_CONVERSIONS_BY_DEFAULT=true QUEUE_CONVERSIONS_AFTER_DB_COMMIT=true
Quick Start
Add the HasMedia trait to your model:
use Jegex\Media\MediaCollections\Concerns\HasMedia; class Post extends Model { use HasMedia; // ... }
Now you can attach media:
$post = Post::create(['title' => 'My Post']); $post->addMedia('/path/to/image.jpg') ->toMediaCollection('images'); $post->getMedia('images')->first()->getUrl();
Usage
Adding Media via Model
Add the HasMedia trait to your model:
use Jegex\Media\MediaCollections\Concerns\HasMedia; class Post extends Model { use HasMedia; // ... }
Now you can attach media:
$post = Post::create(['title' => 'My Post']); $post->addMedia('/path/to/image.jpg') ->toMediaCollection('images'); $post->getMedia('images')->first()->getUrl();
Adding Media Directly (Standalone)
Since the model_type and model_id columns are nullable, you can create media without associating it to any model:
use Jegex\Media\MediaCollections\Models\Media; // From a file path $media = Media::createFromFile('/path/to/image.jpg'); // From string content $media = Media::createFromString($imageContent, 'photo.jpg'); // From base64 $media = Media::createFromBase64($base64String, 'avatar.png'); // From a URL $media = Media::createFromUrl('https://example.com/image.jpg'); // With custom options $media = Media::createFromFile('/path/to/image.jpg', [ 'collection_name' => 'uploads', 'name' => 'Custom Name', 'disk' => 's3', 'custom_properties' => ['source' => 'api'], ]); // Access the media echo $media->getUrl(); echo $media->toHtml();
This is useful for global media libraries, CDN assets, or when you don't need to associate media with a specific model.
Retrieving Media
// Get all media in a collection $media = $model->getMedia('photos'); // Get first media $media = $model->getFirstMedia('photos'); // Get media URL $url = $media->getUrl(); // Get conversion URL $url = $media->getUrl('thumb'); // Get temporary URL (S3) $url = $media->getTemporaryUrl(now()->addMinutes(10));
Media Properties
$media->getName(); // 'my-image' $media->getAltTxt(); // 'Alt text for accessibility' $media->getCaption(); // 'Short caption' $media->getDescription(); // 'Full description' $media->file_name; // 'my-image.jpg' $media->mime_type; // 'image/jpeg' $media->size; // 102400 (bytes)
Custom Properties
$media->setCustomProperty('credits', 'John Doe'); $media->getCustomProperty('credits'); // 'John Doe' $media->hasCustomProperty('credits'); // true
Rendering Media
// Convert to HTML string echo $media->toHtml(); // <img src="..." alt="..." loading="lazy"> // Blade component <x-media :media="$media" class="w-full" :loading="'lazy'" /> // With conversion <x-media :media="$media" conversion="thumb" class="thumbnail" />
Image Conversions
Define conversions in your model:
use Jegex\Media\MediaCollections\Concerns\HasMedia; class Post extends Model { use HasMedia; public function registerMediaConversions(?Media $media = null): void { $this->addMediaConversion('thumb') ->width(200) ->height(200) ->optimize() ->queued(); $this->addMediaConversion('preview') ->width(800) ->height(600) ->optimize(); } }
Retrieve converted images:
$thumbUrl = $media->getUrl('thumb'); $previewUrl = $media->getUrl('preview');
Conversion Methods
| Method | Description |
|---|---|
width($px) |
Set width |
height($px) |
Set height |
fit(Fit $fit) |
Set fit mode (contain, cover, fill, etc.) |
format($format) |
Set output format (webp, avif, jpg, png) |
quality($quality) |
Set output quality (1-100) |
brightness($value) |
Adjust brightness (-100 to 100) |
contrast($value) |
Adjust contrast |
blur($value) |
Apply blur effect |
gamma($value) |
Adjust gamma |
flip($direction) |
Flip image (h, v) |
optimize() |
Optimize output image |
queued() |
Process conversion on queue |
nonQueued() |
Process conversion synchronously |
manipulate($name, $value) |
Add custom manipulation |
Image Generators
The package supports multiple file types out of the box:
- GenericImage — JPEG, PNG, GIF, BMP
- Webp — WebP images
- Avif — AVIF images
- Pdf — PDF files (thumbnail extraction)
- Svg — SVG files
- Video — Video files (thumbnail extraction via FFMPEG)
Image Optimizers
Optimizers are applied automatically when optimize() is called:
| Format | Optimizer |
|---|---|
| JPEG | Jpegoptim |
| PNG | Pngquant, Optipng |
| SVG | Svgo |
| GIF | Gifsicle |
| WebP | Cwebp |
| AVIF | Avifenc |
Responsive Images
Responsive images are generated automatically for image media files.
// Enable responsive images for a collection $model->addMedia('/path/to/image.jpg') ->withResponsiveImages() ->toMediaCollection('photos');
The package calculates optimal widths using FileSizeOptimizedWidthCalculator (30% smaller per variation) and generates a blurred tiny placeholder for progressive loading.
Queue System
Conversions and responsive images can be processed asynchronously.
Configuration
// config/media.php 'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'), 'queue_name' => env('MEDIA_QUEUE', ''), 'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true), 'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true),
Per-Conversion Queue Setting
$this->addMediaConversion('thumb') ->width(200) ->queued(); // Process on queue $this->addMediaConversion('preview') ->width(800) ->nonQueued(); // Process immediately
Jobs
| Job | Description |
|---|---|
PerformConversionsJob |
Processes image conversions |
GenerateResponsiveImagesJob |
Generates responsive image variations |
Vapor Uploads
For Laravel Vapor deployments, enable the upload route:
// config/media.php 'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false), 'vapor_route_prefix' => 'media-vapor', 'vapor_route_middleware' => ['web', 'auth'],
Routes
| Method | Route | Description |
|---|---|---|
| POST | /media-vapor |
Store new media from Vapor |
| POST | /media-vapor/finished/{mediaId} |
Mark media upload as finished |
| POST | /media-vapor/parameters |
Get upload parameters for S3 direct upload |
ZIP Export
Export media collections as ZIP archives for bulk downloads.
Export from Model
// Stream ZIP directly to browser return $post->getMediaCollectionZip('photos')->download('photos.zip'); // Save ZIP to a disk $zipPath = $post->getMediaCollectionZip('photos')->saveToDisk('public', 'exports/photos.zip');
Export from Single Media
use Jegex\Media\MediaCollections\Models\Media; // Get ZIP for a single media item $zip = $media->getZip('archive.zip'); // Or retrieve media by ID first $media = Media::find(1); $zip = $media->getZip('archive.zip');
Filter by Conversion
// Only include 'thumb' conversions in the ZIP return $post->getMediaCollectionZip('photos', function ($zip, $media) { $zip->add($media, 'thumb'); })->download('thumbs.zip');
The ZIP export uses maennchen/zipstream-php for memory-efficient streaming. Files are added directly to the stream without loading them entirely into memory.
Facade Usage
The package provides a LaravelMedia facade for convenient access to media operations without needing a model:
use Jegex\Media\Facades\LaravelMedia; // Create media from file $media = LaravelMedia::createFromFile('/path/to/image.jpg'); // Create media from string content $media = LaravelMedia::createFromString($imageContent, 'photo.jpg'); // Create media from base64 $media = LaravelMedia::createFromBase64($base64String, 'avatar.png'); // Create media from URL $media = LaravelMedia::createFromUrl('https://example.com/image.jpg'); // Retrieve media $media = LaravelMedia::getMediaById(1); $media = LaravelMedia::getMediaByIds([1, 2, 3]); $collection = LaravelMedia::getMediaByCollection('avatars'); // Delete media LaravelMedia::deleteMedia(1); // Get package info $maxSize = LaravelMedia::getMaxFileSize(); // 10485760 (10MB) $defaultDisk = LaravelMedia::getDefaultDisk(); // 'public'
Configuration
The full configuration file (config/media.php):
return [ // Default storage disk 'disk_name' => env('MEDIA_DISK', 'public'), // Maximum file size (10MB default) 'max_file_size' => 1024 * 1024 * 10, // Queue settings 'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'), 'queue_name' => env('MEDIA_QUEUE', ''), 'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true), 'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true), // File naming and path generation 'file_namer' => DefaultFileNamer::class, 'path_generator' => DefaultPathGenerator::class, 'file_remover_class' => DefaultFileRemover::class, 'url_generator' => DefaultUrlGenerator::class, // URL versioning 'version_urls' => false, // Image driver: gd, imagick, vips 'image_driver' => env('IMAGE_DRIVER', 'gd'), // FFMPEG settings 'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'), 'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'), 'ffmpeg_timeout' => env('FFMPEG_TIMEOUT', 900), 'ffmpeg_threads' => env('FFMPEG_THREADS', 0), // Downloads 'media_downloader' => DefaultDownloader::class, 'media_downloader_ssl' => env('MEDIA_DOWNLOADER_SSL', true), // Temporary URL lifetime (minutes) 'temporary_url_default_lifetime' => env('MEDIA_TEMPORARY_URL_DEFAULT_LIFETIME', 5), // S3 upload headers 'remote' => [ 'extra_headers' => [ 'CacheControl' => 'max-age=604800', ], ], // Responsive images 'responsive_images' => [ 'width_calculator' => FileSizeOptimizedWidthCalculator::class, 'use_tiny_placeholders' => true, 'tiny_placeholder_generator' => Blurred::class, ], // Loading attribute: 'lazy', 'eager', 'auto', or null 'default_loading_attribute_value' => null, // Storage prefix 'prefix' => env('MEDIA_PREFIX', ''), // Force lazy loading 'force_lazy_loading' => env('FORCE_MEDIA_LIBRARY_LAZY_LOADING', true), // Vapor uploads 'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false), 'vapor_route_prefix' => 'media-vapor', 'vapor_route_middleware' => ['web', 'auth'], ];
Environment Variables
| Variable | Default | Description |
|---|---|---|
MEDIA_DISK |
public |
Default storage disk |
MEDIA_QUEUE |
'' |
Queue name |
QUEUE_CONNECTION |
sync |
Queue connection |
QUEUE_CONVERSIONS_BY_DEFAULT |
true |
Queue conversions by default |
QUEUE_CONVERSIONS_AFTER_DB_COMMIT |
true |
Run after database commit |
IMAGE_DRIVER |
gd |
Image processing driver |
FFMPEG_PATH |
/usr/bin/ffmpeg |
FFMPEG binary path |
FFPROBE_PATH |
/usr/bin/ffprobe |
FFProbe binary path |
FFMPEG_TIMEOUT |
900 |
FFMPEG timeout (seconds) |
FFMPEG_THREADS |
0 |
FFMPEG thread count |
MEDIA_DOWNLOADER_SSL |
true |
SSL verification for downloads |
MEDIA_TEMPORARY_URL_DEFAULT_LIFETIME |
5 |
Temporary URL lifetime (minutes) |
MEDIA_PREFIX |
'' |
Storage path prefix |
FORCE_MEDIA_LIBRARY_LAZY_LOADING |
true |
Force lazy loading |
ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS |
false |
Enable Vapor upload routes |
Advanced Usage
Custom Path Generator
Create a custom path generator:
namespace App\Support; use Jegex\Media\Support\PathGenerator\DefaultPathGenerator; use Jegex\Media\MediaCollections\Models\Media; class CustomPathGenerator extends DefaultPathGenerator { public function getPath(Media $media): string { return $media->model_type.'/'.date('Y/m/d').'/'.$media->id; } }
Register it in config:
'path_generator' => App\Support\CustomPathGenerator::class,
Or per-model:
'custom_path_generators' => [ App\Models\Post::class => App\Support\PostPathGenerator::class, ],
Media Lifecycle Events
The MediaObserver handles:
- creating: Sets highest order number
- created: Dispatches conversion and responsive image jobs
- updating: Handles file renaming (if
moves_media_on_updateis true) - deleting: Removes all associated files from disk
Blade Component
{{-- Basic usage --}} <x-media :media="$media" /> {{-- With custom class and loading --}} <x-media :media="$media" class="w-full rounded-lg" loading="lazy" /> {{-- With conversion --}} <x-media :media="$media" conversion="thumb" alt="Thumbnail" /> {{-- Override loading attribute --}} <x-media :media="$media" :loading="null" />
Testing
composer test
Run with coverage:
composer test-coverage
Run a specific test:
vendor/bin/pest --filter="test name"
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
The MIT License (MIT). Please see License File for more information.
统计信息
- 总下载量: 6
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 7
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-08