Implementing Multiple File Uploads in Laravel 11: A Complete Guide to Secure Storage
In this article you will learn about, Laravel 11 Multiple File Upload.
Handling multiple file uploads is a critical feature for modern applications, from document management systems to media galleries. With Laravel 11, you can implement this functionality efficiently while ensuring security and scalability. This guide walks through every step, integrating local storage and database registration.
Why Use Multiple File Uploads?
Beyond improving user experience, bulk uploads reduce interaction time and simplify workflows such as:
- Batch submission of financial documents.
- Uploading portfolios with images and PDFs.
- Integration with automated backup systems.
Key Insight: Applications supporting multiple uploads see 40% higher engagement than single-file systems (Source: Smashing Magazine, 2024).
Step 1: Initial Project Setup
System Requirements
- PHP 8.2 or higher (for null safety and fibers).
- Composer 2.6+ (optimized dependency management).
- Enabled
fileinfo
extension (MIME type validation).
Create the Project
composer create-project laravel/laravel file-manager-app
cd file-manager-app
Tip: Use php artisan serve --port=8080
to avoid port conflicts in local environments.
Step 2: Database Modeling
Create the Migration
Generate the migration for the files
table:
php artisan make:migration create_files_table
Define the schema in the generated file (database/migrations/[...]_create_files_table.php
):
public function up(): void
{
Schema::create('files', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('Physical file name');
$table->string('original_name')->comment('Original file name');
$table->string('mime_type')->comment('MIME type (e.g., image/png)');
$table->timestamps();
});
}
Improvement: Additional fields like original_name
and mime_type
enable advanced auditing and filtering.
Run the migration:
php artisan migrate
Step 3: Defining the File Model
Create the model and specify fillable attributes:
php artisan make:model File
In app/Models/File.php
:
protected $fillable = [
'name',
'original_name',
'mime_type'
];
Security Note: Avoid including sensitive fields like user_id
in $fillable
. Use controlled mass assignment.
Step 4: Developing the Controller
Generate the Controller
php artisan make:controller FileController
Upload Logic (store Method)
In app/Http/Controllers/FileController.php
:
public function store(Request $request)
{
$validated = $request->validate([
'files' => 'required|array|max:10',
'files.*' => 'required|file|mimes:pdf,xlsx,csv,png,jpeg|max:5120',
]);
$uploadedFiles = [];
foreach ($request->file('files') as $file) {
$originalName = $file->getClientOriginalName();
$hashedName = Str::random(40) . '.' . $file->extension();
$mimeType = $file->getMimeType();
// Store in the 'public/uploads' directory
$file->storeAs('uploads', $hashedName, 'public');
$uploadedFiles[] = [
'name' => $hashedName,
'original_name' => $originalName,
'mime_type' => $mimeType
];
}
File::insert($uploadedFiles);
return back()
->with('success', count($uploadedFiles) . ' files saved!')
->with('fileList', $uploadedFiles);
}
Optimizations:
- Hashed filenames: Prevent conflicts and unauthorized access.
- Upload limits: Maximum of 10 files per request (
max:10
). - Disk storage: Uses the
public
driver for easy future CDN integration.
Step 5: Configuring Routes
In routes/web.php
:
use App\Http\Controllers\FileController;
Route::controller(FileController::class)
->prefix('files')
->group(function () {
Route::get('/upload', 'index')->name('files.form');
Route::post('/upload', 'store')->name('files.store');
});
Advantage: Route grouping simplifies maintenance and middleware application.
Step 6: Building the Blade View with Bootstrap
In resources/views/fileUpload.blade.php
:
<div class="container">
<div class="card shadow-lg mt-5">
<div class="card-header bg-primary text-white">
<i class="fas fa-cloud-upload-alt"></i> Multiple File Upload
</div>
<div class="card-body">
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
<ul class="mt-2">
@foreach (session('fileList') as $file)
<li>{{ $file['original_name'] }} ({{ $file['mime_type'] }})</li>
@endforeach
</ul>
</div>
@endif
<form method="POST" action="{{ route('files.store') }}" enctype="multipart/form-data">
@csrf
<div class="mb-4">
<label class="form-label">
<i class="fas fa-folder-open"></i> Select Files
</label>
<input
type="file"
name="files[]"
multiple
class="form-control"
accept=".pdf,.xlsx,.csv,.png,.jpeg"
>
<small class="form-text text-muted">
Allowed formats: PDF, Excel, CSV, PNG, JPEG (Max 5MB per file).
</small>
</div>
<button type="submit" class="btn btn-success">
<i class="fas fa-upload"></i> Upload
</button>
</form>
</div>
</div>
</div>
Additional Features:
- Detailed feedback: Lists uploaded files with MIME types.
- Accessibility: Font Awesome icons for better UX.
Step 7: Testing and Execution
Start the server:
php artisan serve
Visit http://localhost:8000/files/upload
and test scenarios like:
- Success: 5 files within size limits.
- Failure: Uploading a
.exe
file (blocked by validation). - Overload: Submitting 11 files (triggers limit error).
Future Enhancements (Roadmap)
- Cloud Storage: Integrate with Amazon S3 or DigitalOcean Spaces.
- Upload Progress Bar: Implement using JavaScript and Laravel Echo.
- File Compression: Auto-resize images via
intervention/image
. - Security Scans: Virus checks using ClamAV or VirusTotal API.
Conclusion
This guide demonstrates how to implement multiple file uploads in Laravel 11 with professional techniques like filename hashing, granular validation, and optimized storage. To scale further:
- Use async jobs for background processing.
- Add retention policies for automatic deletion of old files.
- Implement detailed logs for activity auditing.
With these strategies, your application will handle high-volume scenarios while maintaining performance and security.
Thanks for reading, Laravel 11 Multiple File Upload 🚀