Secure Uploads - v1.0.0

Middleware

The service provider registers two middleware aliases for routes that handle uploads directly (without going through HasSecureFiles::attachSecureFile(), which runs the same checks internally).

validate.upload

Runs FileValidationService over every file in the request before the controller runs.

Route::post('/uploads', [UploadController::class, 'store'])
    ->middleware('validate.upload');

Failed validation returns a 422 response with the errors. Successful validation lets the request through unchanged — the controller sees the same UploadedFile instances.

scan.upload

Runs the configured malware scanner over every file in the request.

Route::post('/uploads', [UploadController::class, 'store'])
    ->middleware(['validate.upload', 'scan.upload']);

Apply in that order — there's no point scanning a file that's going to fail validation. Failed scans return 422 with the MalwareDetected info; clean scans let the request through.

Combined with rate limiting

use ArtisanPackUI\SecureUploads\Services\FileUploadRateLimiter;

Route::post('/uploads', [UploadController::class, 'store'])
    ->middleware([
        'validate.upload',
        'scan.upload',
        FileUploadRateLimiter::middleware(),
    ]);

See Rate limiting for the rate limiter's keying and tuning.

When to use middleware vs the trait

Use middleware when... Use the trait when...
You want validation to happen before any controller logic runs The upload is part of a larger controller flow (validate the form, run business logic, then attach the file)
The controller doesn't need a SecureUploadedFile row — just wants the raw file You want the file attached to an Eloquent model and a row in secure_files
You're integrating with an existing upload controller you don't want to rewrite You're building a new feature on top of HasSecureFiles from scratch

Both paths run the same checks. The trait does more work (storing the file, creating the row) but gives you a typed result and a relationship for follow-up queries.