How to Upload Multiple Files in Laravel 12
Iutorial, we will learn how to upload multiple files in Laravel 12, store file information in the MySQL database, and use Laravel's logging system with session-based file group naming.
Step 1: Create a Migration for the uploads
Table
Run the migration command:
php artisan make:migration create_uploads_table
Then define your table structure in the generated migration:
// database/migrations/xxxx_xx_xx_create_uploads_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('uploads', function (Blueprint $table) {
$table->id();
$table->string('filename');
$table->string('upload_name')->nullable();
$table->timestamp('uploaded_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('uploads');
}
};
Then run:
php artisan migrate
Step 2: Create the Upload Model
php artisan make:model Upload
Edit the model to allow mass assignment:
// app/Models/Upload.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Upload extends Model
{
protected $fillable = ['filename', 'upload_name', 'uploaded_at'];
}
Step 3: Create the Upload Controller
php artisan make:controller FormController
Step 4: Define Routes
In routes/web.php
:
// ------------------------- Form Upload ----------------------------// Route::controller(FormController::class)->group(function () { Route::middleware('auth')->group(function () { Route::get('form/upload/page', 'index')->name('form.upload.page'); Route::post('form/upload/save', 'storeFileUpload')->name('form.upload.save'); }); });
Step 5: Create the Blade Upload Form
<!-- resources/views/upload.blade.php --> @extends('layouts.master') @section('style') <style> .file-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding-bottom: 6px; border-bottom: 1px solid #eee; } .file-name { flex: 1; color: #444; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .icon { display: inline-block; font-size: 1.2em; line-height: 1; margin-right: 0.5em; } .icon.success { color: green; padding-left: 8px; } .icon.remove { color: red; cursor: pointer; } </style> @endsection @section('content') <div class="row"> <div class="col-lg-12 col-md-8 col-sm-12 mx-auto"> <div class="d-flex justify-content-between align-items-center mb-3"> <div class="fw-bold py-2">Page Form</div> <div class="p-2"> Page Form - <a href="{{ route('home') }}" class="text-decoration-none fw-semibold">Dashboard</a> </div> </div> <div class="card p-3 shadow-sm"> <div class="container py-3"> <form id="uploadForm" action="{{ route('form/upload/save') }}" method="POST" enctype="multipart/form-data"> @csrf <div class="mb-3 row"> <label for="fileupload" class="form-label">Upload Files</label> <div class="col-sm-3"> <input type="file" class="form-control" id="fileupload" name="fileupload[]" multiple> </div> </div> <div class="mb-3 row"> <div class="col-12"> <div id="file-list"></div> </div> </div> <div class="mb-3 row"> <div class="col-sm-3"> <button type="submit" class="btn btn-primary">Submit</button> </div> </div> </form> </div> </div> </div> </div> @endsection @section('script') <script> const $input = $('#fileupload'); const $list = $('#file-list'); const files = []; const formatSize = bytes => bytes < 1024 ? `${bytes} B` : `${(bytes / 1024).toFixed(1)} Kb`; $input.on('change', function () { Array.from(this.files).forEach(file => { const exists = files.some(f => f.name === file.name && f.size === file.size); if (!exists) { files.push(file); } }); this.value = null; renderFileList(); }); $list.on('click', '.remove', e => { const index = $(e.target).data('idx'); files.splice(index, 1); renderFileList(); }); function renderFileList() { const dataTransfer = new DataTransfer(); $list.empty(); files.forEach((file, index) => { dataTransfer.items.add(file); $list.append(` <div class="file-row"> <span class="file-name">${file.name}</span> <span class="file-size">${formatSize(file.size)}</span> <span class="icon success">✓</span> <span class="icon remove" data-idx="${index}">✕</span> </div> `); }); $input[0].files = dataTransfer.files; } </script> @endsection
Step 6: Implement the Store Logic
// app/Http/Controllers/FormController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
use App\Models\Upload;
use Carbon\Carbon;
class FormController extends Controller
{
/**
* Display the file upload form.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('form.upload');
}
/**
* Handle file upload and save to the database.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function storeFileUpload(Request $request)
{
// Validate the uploaded files
$request->validate([
'fileupload.*' => 'required|file|mimes:jpg,jpeg,png,pdf,docx|max:2048'
]);
try {
// Check if files are present in the request
if ($request->hasFile('fileupload')) {
foreach ($request->file('fileupload') as $file) {
// Create a unique filename with timestamp to avoid filename conflicts
$filename = time() . '_' . $file->getClientOriginalName();
// Store the file in the 'uploads' directory within the public disk
$path = $file->storeAs('uploads', $filename, 'public');
// Save file info to the database
Upload::create([
'filename' => $filename,
'upload_name' => Auth::user()->name,
'uploaded_at' => Carbon::now()
]);
}
}
return back()->with('success', 'Files uploaded and saved successfully.');
} catch (\Exception $e) {
// Log any exception that occurs during the upload process
Log::error('File upload failed: ' . $e->getMessage());
return back()->with('error', 'Something went wrong while uploading the files.');
}
}
}
Step 7: Create the uploads
Directory
Make sure this directory exists in your project:
mkdir public/uploads
chmod -R 775 public/uploads
Step 8: Test Your Application
-
Visit
/set-upload-name
to set the session upload name. -
Go to
/upload
and upload multiple files. -
Check the a
uploads
table in MySQL to confirm the records. -
Review
storage/logs/laravel.log
for success/error logs.
Conclusion
In this tutorial, you learned how to:
-
Upload multiple files in Laravel 12.
-
Store file names, upload batch name (from session), and timestamps in MySQL.
-
Use
try-catch
blocks for error handling. -
Write logs using Laravel's
Log
facade.