How to do an image upload form and handler in Laravel 9 with validation.
Included is a controller, upload form and migration for the database table to store information about the image upload.
Create the image upload controller with
php artisan make:controller ImageUploadController
This will handle showing the upload form blade file and the processing of the image upload (validation, renaming and storing).
Inside app/Http/Controllers/ImageUploadController.php paste the following:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ImageUploadController extends Controller
{
public function index()
{
return view('images.upload');
}
public function store(Request $request)
{
$request->validate([
'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
]);
$original_name = $request->image->getClientOriginalName();//Original image file name
$file_size = $request->image->getSize();//Size in bytes
$extension = $request->image->extension();//Image file extension eg. jpg or png
$directory = 'uploads/' . date('Y') . '/' . date('m');//Upload and move image into this directory
$id = Str::random(8);//Random 8 character string
$image_name = $id . '.' . $extension;
$full_save_path_name = $directory . '/' . $image_name;
$request->image->move(public_path($directory), $image_name);//Save into: public/images
DB::table('images')->insert([
'id' => $id,
'user_id' => Auth::id(),
'size' => $file_size,
'original_name' => $original_name,
'directory' => $directory,
'extension' => $extension,
'uploaded' => date('Y-m-d H:i:s')
]);
return back()
->with("success", "You have successfully uploaded $original_name saved as: $image_name")
->with("image", $full_save_path_name);
}
}
The uploaded image will be validated with 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048' this means the form field is required, it must be an image in jpg/png/gif/svg format and the maximum allowed size is 2048 kilobytes.
The save directory will be public/uploads/YEAR/MONTH e.g public/uploads/2022/04
The image will be renamed as a randomly generated id, 8 characters long.
In routes/web.php add:
Route::controller(ImageUploadController::class)->middleware('auth')->group(function(){
Route::get('image-upload', 'index');
Route::post('image-upload', 'store')->name('image.store');
});
These two routes are protected by ‘auth’ meaning only logged in users can view them. The two routes are /image-upload as a GET which calls index and /image-upload as a POST which calls the store method from ImageUploadController.
Run php artisan route:cache to refresh the routes.
This will be the form upload page as a blade file, put in resources/views/images the file upload.blade.php with the following:
<!DOCTYPE html>
<html>
<head>
<title>Upload image</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="card">
<div class="card-body">
<div class="panel panel-primary">
<div class="panel-heading">
<h2>Upload image</h2>
</div>
<div class="panel-body">
@if ($message = Session::get('success'))
<div class="alert alert-success alert-block">
<strong>{{ $message }}</strong>
</div>
<img class="img-fluid" src="{{ Session::get('image') }}" alt="uploaded image">
@endif
<form action="{{ route('image.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label class="form-label" for="image_input">Image:</label>
<input type="file" name="image" id="image_input"
class="form-control @error('image') is-invalid @enderror">
@error('image')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success">Upload</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html> Normally this blade file will be an extension of the main layout however for this example it encompasses the whole page with the header and CSS source.
This is if you want to insert details about the image upload into the database.
If you do not want to do this skip this section and delete the DB::table('images')->insert... section of the ImageUploadController store method.
Run: php artisan make:migration images
Find the database/migrations create_images_table.php file and paste:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->char('id', 8);
$table->bigInteger('user_id');
$table->integer('size');
$table->string('original_name');
$table->string('directory');
$table->string('extension');
$table->date('uploaded');
$table->primary(['id']);
});
}
public function down()
{
Schema::dropIfExists('images');
}
}; Run migrate to create this table: php artisan migrate
Once logged in navigate to /image-upload
If you upload a non-image you get this notice
or if the image is oversized (greater than 2048 Kilobytes)
Upon a successful upload:
A drained and empty Kennington reservoir images from a drone in early July 2024. The…
Merrimu Reservoir from drone. Click images to view larger.
Using FTP and PHP to get an array of file details such as size and…
Creating and using Laravel form requests to create cleaner code, separation and reusability for your…
Improving the default Laravel login and register views in such a simple manner but making…
Laravel validation for checking if a field value exists in the database. The validation rule…