Skip to content
Laravel package

Laravel Filex

A drop-in Blade file uploader for Laravel with chunked uploads, server-side validation and automatic temp-file cleanup.

composer require devwizardhq/laravel-filex
View on GitHub
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-uploader for 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 HasFilex controller trait, a Filex facade and a FileRule facade 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

Terminal
composer require devwizardhq/laravel-filex

Run the installer to publish the config file and the CSS and JavaScript assets:

Terminal
php artisan filex:install

The installer accepts --force, --only-config, --only-assets and --auto. You can also publish manually with the tags filex-config and filex-assets:

Terminal
php artisan vendor:publish --tag=filex-config
php artisan vendor:publish --tag=filex-assets

Add the assets directive once to your main layout, for example just before </body>:

Blade
@filexAssets

Configuration

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.

Blade
<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:

PHP
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:

PHP
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:

PHP
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:

PHP
'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.