On this page
Overview
Laravel Filex solves the two halves of file uploads in one package. On the page it gives you an <x-filex-uploader> Blade component with drag and drop, previews, progress and retries. On the server it gives you routes that accept each file into temporary storage, validate it, and later move it to its final disk when your form is submitted.
Because files are uploaded and checked before the form is posted, your controller only receives temporary paths. It then moves them into permanent storage with one method call and no multipart handling in your own code. Filex is maintained by the DevWizard core team and released under the MIT license.
Features
- Blade component
x-filex-uploaderfor single and multiple uploads, with previews, progress and retry handling. - Chunked uploads for large files, with configurable chunk size, retry count and timeout.
- Temporary storage on a separate disk, with scheduled cleanup of expired files.
- Server-side validation of extensions, MIME types, size limits and image dimensions.
- Security layer: suspicious extension and content detection, file signature validation, a quarantine directory, and per-IP and per-user rate limiting.
- A
HasFilexcontroller trait, aFilexfacade and aFileRulefacade for validation rules. - Public or private visibility per move operation.
- Bundled translations for 11 languages: Arabic, Bengali, German, English, Spanish, French, Hindi, Italian, Japanese, Portuguese, Russian and Chinese.
Requirements
- PHP 8.2 or newer.
- Laravel 11, 12 or 13 (
illuminate/contracts^11.0||^12.0||^13.0). - A configured filesystem disk for final storage and one for temporary files.
Installation
composer require devwizardhq/laravel-filexRun the installer to publish the config file and the CSS and JavaScript assets:
php artisan filex:installThe installer accepts --force, --only-config, --only-assets and --auto. You can also publish manually with the tags filex-config and filex-assets:
php artisan vendor:publish --tag=filex-config
php artisan vendor:publish --tag=filex-assetsAdd the assets directive once to your main layout, for example just before </body>:
@filexAssetsConfiguration
Publishing creates config/filex.php. Most options read from environment variables, so you can tune behavior per environment without editing the file.
| Key | Environment variable | Default | Purpose |
|---|---|---|---|
storage.disks.default |
FILEX_DISK |
app default disk | Disk for final files |
storage.disks.temp |
FILEX_TEMP_DISK |
local |
Disk for temporary uploads |
storage.max_file_size |
FILEX_MAX_SIZE |
10 (MB) |
Global size ceiling |
storage.temp_expiry_hours |
FILEX_TEMP_EXPIRY |
24 |
Age after which temp files are removed |
storage.visibility.default |
FILEX_DEFAULT_VISIBILITY |
public |
public or private |
upload.chunk.size |
FILEX_CHUNK_SIZE |
1048576 (1 MB) |
Chunk size in bytes |
upload.chunk.max_retries |
FILEX_CHUNK_RETRIES |
3 |
Retries per chunk |
routes.prefix |
FILEX_ROUTE_PREFIX |
filex |
URL prefix for upload routes |
routes.middleware |
FILEX_ROUTE_MIDDLEWARE |
none | Extra middleware, such as auth |
system.cleanup.enabled |
FILEX_CLEANUP_ENABLED |
true |
Schedule filex:cleanup-temp |
performance.rate_limiting.ip_limit |
FILEX_RATE_IP_LIMIT |
50 |
Uploads per IP per window |
performance.rate_limiting.user_limit |
FILEX_RATE_USER_LIMIT |
100 |
Uploads per user per window |
security.quarantine.retention_days |
FILEX_QUARANTINE_RETENTION_DAYS |
30 |
How long quarantined files are kept |
The validation.allowed_extensions and validation.allowed_mime_types arrays define what Filex accepts by default. Trim them to the types your application actually needs.
Usage
Place the component in any form. Filex uploads the file as soon as it is chosen and stores the temporary path in a hidden input named after the name prop.
<form method="POST" action="/documents" enctype="multipart/form-data">
@csrf
<x-filex-uploader name="documents" :multiple="true" :maxFiles="5" maxSize="10" mimes="pdf,docx" />
<button type="submit">Save</button>
</form>In the controller, add the HasFilex trait, validate, and move the files to permanent storage:
use DevWizard\Filex\Traits\HasFilex;
class DocumentController extends Controller
{
use HasFilex;
public function store(Request $request)
{
$request->validate([
'title' => 'required|string',
...$this->getFilesValidationRules('documents', true),
]);
$paths = $this->moveFiles($request, 'documents', 'documents/user-uploads');
Document::create(['title' => $request->title, 'file_paths' => $paths]);
}
}For private files, use moveFilePrivate() or moveFilesPrivate(), or pass 'private' as the visibility argument.
API Reference
Blade component and directive
<x-filex-uploader> props include name, multiple, accept, maxFiles, maxSize, minSize, required, disabled, readonly, rules, mimes, extensions, dimensions, clientValidation, label, helpText, placeholder, showProgress, showFileSize, allowPreview, autoProcess, parallelUploads, chunkSize, retries and timeout. Callback props onSuccess, onError, onComplete, onFileAdded, onFileRemoved and onUpload take the name of a JavaScript function. @filexAssets loads the CSS, JavaScript and route configuration.
HasFilex trait
All methods are protected, for use inside controllers.
| Method | Purpose |
|---|---|
moveFile($request, $field, $directory, $disk = null, $visibility = null) |
Move one file, returns the stored path or null |
moveFiles(...) |
Move many files, returns an array of paths |
moveFilePublic(), moveFilePrivate(), moveFilesPublic(), moveFilesPrivate() |
Same, with fixed visibility |
getFileValidationRules($field, $required = false) |
Rules for a single file field |
getFilesValidationRules($field, $required = false) |
Rules for a multi-file field |
cleanupTempFiles(array $tempPaths) |
Delete temp files, returns the count |
Filex facade
Filex::moveFile(), moveFiles(), moveFilePublic(), moveFilePrivate(), moveFilesPublic(), moveFilesPrivate(), generateFileName(), validateTemp() and cleanup(). The move methods return a FilexResult with isSuccess(), isAllSuccess(), getPath(), getPaths(), getFailed(), getErrorMessage(), getErrorMessages(), getMetadata(), getSuccessCount(), getFailedCount(), toArray() and toJson().
FileRule facade
Returns a ValidFileUpload rule: forImages($maxSizeMB = 5), forDocuments(10), forArchives(50), forAudio(20), forVideo(100), forWeb(10), forType($extension, $mimeType, $maxSizeMB), custom($extensions, $mimeTypes, $maxSizeMB, $strict) and lenient(...).
FilexRule helper
DevWizard\Filex\Support\FilexRule offers mimes(), mimetypes(), min(), max(), size(), dimensions(), image(), file(), plus rules() and parse() to convert rule strings.
Artisan commands
| Command | Options |
|---|---|
filex:install |
--force, --only-config, --only-assets, --auto |
filex:cleanup-temp |
--force, --dry-run, --quarantine-only |
filex:optimize |
--clear-cache, --analyze, --config-check |
filex:info |
none |
HTTP routes
Under the configured prefix (default /filex): POST /upload-temp, POST /upload-temp-optimized, DELETE /temp/{filename}, GET /temp/{filename}/info and GET /config.
Examples
Validate uploaded temp paths with the facade rules, one rule per file type:
use DevWizard\Filex\Facades\FileRule;
$request->validate([
'photos.*' => ['required', 'string', FileRule::forImages(5)],
'contract' => ['required', 'string', FileRule::forDocuments(10)],
]);Move files yourself with the facade and branch on the result:
use DevWizard\Filex\Facades\Filex;
$result = Filex::moveFilesPrivate($tempPaths, 'invoices');
if (! $result->isAllSuccess()) {
return back()->withErrors($result->getErrorMessages());
}
$order->update(['attachments' => $result->getPaths()]);Require authentication on the upload routes by setting the middleware in your environment or config:
'routes' => [
'prefix' => 'filex',
'middleware' => ['web', 'auth'],
],FAQ & Troubleshooting
The uploader looks unstyled or does nothing. Make sure @filexAssets is in your layout and that you published the assets with php artisan vendor:publish --tag=filex-assets.
Uploads of large files fail. Raise FILEX_CHUNK_RETRIES or FILEX_CHUNK_TIMEOUT, and check your web server and PHP limits for request size, since each chunk is a request.
A file type is rejected. Add its extension to validation.allowed_extensions and its type to validation.allowed_mime_types in config/filex.php. Extensions listed under security.suspicious_extensions, such as php, are treated as dangerous and can be quarantined.
Users get “too many upload attempts”. Raise FILEX_RATE_IP_LIMIT or FILEX_RATE_USER_LIMIT, or disable limiting with FILEX_RATE_LIMITING_ENABLED=false.
Temp files pile up. Cleanup is scheduled automatically while FILEX_CLEANUP_ENABLED is true, which requires Laravel’s scheduler to be running. You can also run php artisan filex:cleanup-temp --dry-run to preview what would be removed.
Changelog & Support
Release notes live in the CHANGELOG and on the GitHub releases page. Report bugs or request features through GitHub issues. Source code: DevWizardHQ/laravel-filex. If you need help integrating Filex into a larger project, the DevWizard team takes project contracts and monthly retainers; see the contact page.