Laravel Uploads for simple, secure file storage
ghostcompiler/laravel-uploads is a Laravel package for laravel uploads for simple, secure file storage.
It currently has 17 GitHub stars and 1.678 downloads on Packagist (latest version v1.1.9).
Install it with composer require ghostcompiler/laravel-uploads.
Discover more Laravel packages by ghostcompiler
or browse all Laravel packages to compare alternatives.
Last updated
Laravel Uploads stores files through Laravel Storage, tracks upload metadata, generates secure private file URLs, supports public disk URLs, integrates with Eloquent models, and can optionally optimize browser images.
composer require ghostcompiler/laravel-uploads
php artisan install:laravel-uploads
php artisan migrate
Use --force to overwrite already-published config or migration files:
php artisan install:laravel-uploads --force
By default, files are stored under LaravelUploads on the configured disk.
use GhostCompiler\LaravelUploads\Facades\Uploads;
$upload = Uploads::upload($request->file('avatar'));
$upload = Uploads::upload('avatars', $request->file('avatar'));
Store the upload ID on your model:
$user->avatar_id = $upload->id;
$user->save();
Delete an upload:
Uploads::remove($user->avatar_id);
The helper uses the same service:
$upload = GhostCompiler()->upload('avatars', $request->file('avatar'));
Add upload ID columns such as avatar_id, document_id, or favicon_id to your own models.
use GhostCompiler\LaravelUploads\Concerns\LaravelUploads;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use LaravelUploads;
protected $uploadable = [
'avatar_id' => [
'name' => 'avatar',
'id' => 'hide',
'expiry' => 60,
],
];
}
Now $user->avatar returns the file URL. In array or JSON responses, URL fields are included by default. Set expose => false on a field when you do not want that URL serialized.
Customize returned values when needed:
public function setUploadableValue($value, string $column, array $options)
{
if ($column === 'avatar_id') {
return $value;
}
return [
'url' => $value,
'name' => $options['name'],
];
}
You can also define field-specific hooks when a model has multiple uploadable fields:
public function setAvatarUploadableValue($value)
{
return $value;
}
public function setResumeUploadableValue($value)
{
return [
'url' => $value,
'type' => 'resume',
];
}
Use $user->avatar for direct access. Use $user->toArray() or API responses when you want the configured uploadable URL fields serialized.
Private uploads generate package URLs like:
https://your-app.test/_laravel-uploads/file/{token}
Public uploads return the disk URL directly and do not create or regenerate laravel_uploads_links rows:
$upload = Uploads::upload('avatars', $request->file('avatar'), [
'visibility' => 'public',
]);
For multi-tenant apps where each tenant has a different domain, register a public URL resolver:
use GhostCompiler\LaravelUploads\Facades\Uploads;
public function boot(): void
{
Uploads::resolvePublicUrlsUsing(function ($upload, $disk, $path) {
$tenant = tenant();
return "https://{$tenant->domain}/storage/{$path}";
});
}
You can also configure a resolver class:
'urls' => [
'public_resolver' => App\Support\TenantUploadUrlResolver::class,
],
class TenantUploadUrlResolver
{
public function publicUrl($upload, $disk, string $path): string
{
return 'https://'.tenant()->domain.'/storage/'.$path;
}
}
Force a private file download with ?download=1.
Use the normal upload API and pass favicon only when the upload should be treated as a favicon:
$upload = Uploads::upload('favicons', $request->file('favicon'), [
'favicon' => true,
]);
Existing .ico files are stored without conversion. JPEG, PNG, and WEBP uploads are converted into a square favicon PNG.
Upload multiple files:
$uploads = Uploads::uploadMany($request->file('documents'), 'documents');
Allow specific excluded extensions only when you explicitly need it:
$upload = Uploads::upload($request->file('script'), ['sh', 'rb']);
Critical extensions in validation.never_allowed_extensions, such as php, phar, and phtml, cannot be allowed with this override.
Clean expired private URL tokens:
php artisan ghost:laravel-uploads-clean
php artisan ghost:laravel-uploads-clean --dry-run
Image optimization is disabled by default.
'image_optimization' => [
'enabled' => true,
'strict' => false,
'quality' => 75,
'convert_to_avif' => true,
'max_width' => 1600,
'max_height' => null,
]
When enabled, supported JPEG, PNG, and WEBP uploads try AVIF first and fall back to WEBP. Resizing preserves aspect ratio and never upscales.
Important config keys:
disk: Laravel disk used for storage.base_path: base folder inside the disk.defaults.visibility: default private or public visibility.defaults.expiry: private URL expiry in minutes.defaults.expose: whether model serialization appends URL fields by default.cache.enabled: reuse private generated URLs until expiry.cache.registry_ttl: minutes to keep the internal cache-key registry used for deleting cached private URLs. Defaults to 60.validation.max_size: max upload size in bytes.validation.allowed_mime_types: optional server-detected MIME allowlist.validation.allowed_extensions: optional extension allowlist.validation.excluded_mime_types: blocked MIME types.validation.excluded_extensions: blocked extensions.validation.never_allowed_extensions: critical extensions that cannot be overridden.image_optimization.*: image conversion and resize limits.favicon.size: favicon output size.downloads.use_original_name: use original filename in download headers.urls.public_resolver: optional tenant/CDN resolver for public upload URLs.preview_mime_types: MIME types allowed to open inline.delete_files_with_model: delete stored files when the model is deleted.route.*: package file-serving route settings. route.middleware defaults to [] so app web middleware cannot redirect image/file requests.Full local development, path repository, validation, and security notes live in DEVELOPER.md.
Release history is documented in CHANGELOG.md.
...expose => false.This package was developed using ServBay as the local development environment.
ServBay
Mac M4Mac M4