Trong phần trước, mình đã chia sẻ cách upload file lên Amazon S3 hoặc MinIO và chia sẻ file tạm thời bằng Temporary URL – đảm bảo bảo mật và kiểm soát quyền truy cập.
Tuy nhiên, với các file ảnh, hiển thị ảnh gốc (full-size) trong giao diện admin hay danh sách upload là không tối ưu:
-
Ảnh có thể quá lớn, làm tăng thời gian tải trang
-
Giao diện sẽ bị vỡ bố cục nếu không kiểm soát kích thước
👉 Vì vậy, trong phần này, bạn sẽ học cách tạo thumbnail (ảnh thu nhỏ) ngay sau khi upload ảnh, sử dụng thư viện Intervention Image – giúp hệ thống gọn nhẹ và chuyên nghiệp hơn.
I. Intervention Image là gì?
Intervention Image là một thư viện PHP phổ biến giúp xử lý ảnh dễ dàng: resize, crop, thêm watermark, và nhiều chức năng khác. Laravel hỗ trợ thư viện này rất tốt thông qua Facade Image.
Trong bài này, mình sẽ dùng Intervetion Image để tạo ảnh thu nhỏ (thumbnail) – một tính năng thường thấy trong hệ thống quản lý ảnh hoặc file upload.
II. Cài đặt Intervention Image
Mình sẽ sử dụng phiên bản Intervention Image v3 - phiên bản mới nhất.
1. Yêu cầu hệ thống
Yêu cầu hệ thống để sử dụng Intervention Image
-
PHP >= 8.1
-
Mbstring PHP Extension
-
Image Processing PHP Extension
Mặc định khi cài Herd, PHP sẽ là bản mới nhất 8.4, hỗ trợ sẵn Mbstring PHP Extenstion và Image Process PHP Extention (Imagick).
Có thể kiểm tra PHP có Mbstring PHP Extension và Imagick chưa bằng lệnh sau
php -m | grep 'mbstring\|imagick'Nếu thấy trả về kết quả như bên dưới nghĩa là hệ thống đã sẵn sàng để cài đặt Intervetion Image
imagickmbstring2. Cài đặt Intervention Image
Cài đặt bằng composer
composer require intervention/imageIII. Tạo thumbnail khi upload ảnh
1. Cấu hình driver xử lý ảnh
Để có thể sử dụng Intervention Image, trước tiên cần phải import class ImageManager và chọn driver (Gd hoặc Imagick). Xem chi tiết hướng dẫn: https://image.intervention.io/v3/basics/configuration-drivers
use Intervention\Image\Drivers\Imagick\Driver;use Intervention\Image\ImageManager;
$manager = new ImageManager(Driver::class);2. Tạo thumbnail khi upload ảnh
Mình bổ sung đoạn code sau vào hàm store trong UploadController để tạo file thumnail có kích thước 100px x 100px
// Tạo thumbnail bằng Intervention Image $thumbnail = $manager->read($file->getRealPath()) ->resize(100, 100);
// Đường dẫn tương đối của file thumbnail: 'uploads/thumbnail-ten_file_cuoi_cung.jpg' $thumbnailStoragePath = $directory . '/thumbnail-' . $finalFilename;
// Lưu thumbnail vào disk Storage::disk($disk)->put($thumbnailStoragePath, $thumbnail->encode());Upload thử 1 file, trên MinIO giờ sẽ tự động có thêm file thumbnail-xxx

3. Hiển thị thumbnail
Để hiển thị thumbnail của file ảnh vừa upload, mình sẽ cập nhật lại $urlFilePath như sau, thay thế $storedFilePath (path của file gốc) bằng $thumbnailStoragePath (path của file thumbnail)
// Trả về Temporary URL của file $urlFilePath = Storage::disk($disk)->temporaryUrl($thumbnailStoragePath, now()->addMinutes(5));Ảnh thumbnail 100 x 100 px giờ đã hiện ra ở phần thông báo Sucess!

Tuy nhiên, danh sách Previously Uploaded Files vẫn hiển thị ảnh gốc do trong DB chưa có thông tin của các file thumbnail được tạo ra.
IV. Lưu đường dẫn thumbnail vào database
Hiện tại, chúng ta chỉ đang lưu đường dẫn của file gốc (filename) vào table uploads. Nếu muốn hiển thị thumbnail dễ dàng, cần phải thêm một cột thumbnail vào table này.
1. Tạo migration file
php artisan make:migration add_thumbnail_to_uploads_table --table=uploadsChỉnh sửa file migration
public function up(): void { Schema::table('uploads', function (Blueprint $table) { $table->string('thumbnail')->nullable(); }); }Chạy migration để áp dụng những thay đổi cho database
php artisan migrate2. Cập nhật model Upload
Trong app/Models/Upload.php, thêm thumbnail vào $fillable:
protected $fillable = [ 'filename', 'original_filename', 'thumbnail',];3. Lưu thumbnail path vào database
Trong hàm store(), cập nhật phần tạo bản ghi như sau:
// Tạo bản ghi mới trong table uploads của database Upload::create([ 'filename' => $storedFilePath, 'original_filename' => $originalFilename, 'thumbnail' => $thumbnailStoragePath,' ]);Đồng thời cập nhật lại hàm destroy để hệ thống xóa luôn file thumbnail mỗi khi bấm nút Delete
if (Storage::disk($disk)->exists($upload->thumbnail)) { Storage::disk($disk)->delete($upload->thumbnail); }4. Chỉnh sửa blade để hiển thị ảnh thumbnail
Trong app/Models/Upload.php, tạo thêm hàm getThumbnailUrlAttribute để có thể truy xuất URL của thumbnail thông qua thuộc tính thumbnail_url
// Tạo URL tạm thời mới mỗi khi thuộc tính 'thumbnail_url' được truy cập public function getThumbnailUrlAttribute(): string { $disk = 'minio'; // Ensure this matches the disk used for storing thumbnails if ($this->thumbnail) { // Generate temporary URL for the thumbnail path return Storage::disk($disk)->temporaryUrl($this->thumbnail, now()->addMinutes(5)); } return ''; // Hoặc xử lý một cách thích hợp nếu tên tệp là null }Cập nhật lại upload.blade.php để hiển thị file thumbnail
@if (count($uploads) > 0) <div class="container mx-auto mt-10 p-10 bg-white rounded-lg shadow-md max-w-md"> <h2 class="text-xl font-semibold mb-4 text-gray-700">Previously Uploaded Files:</h2> @foreach ($uploads as $upload) <ul> <li class="flex items-center justify-between mb-4"> <a class="flex items-center gap-4 py-2" href="{{ $upload->url }}" target="_blank"> <img src="{{ $upload->thumbnail_url }}" alt="{{ $upload->original_filename }}" width="50" height="50"> <span class="text-sm text-gray-700 hover:text-blue-600">{{ $upload->original_filename }}</span> </a> <form action="{{ route("upload.destroy", $upload->id) }}" method="POST" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this file?');"> @csrf @method("DELETE") <button type="submit" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Delete</button> </form> </li> </ul> @endforeach </div> @endifThử nghiệm lại, tất cả đã hoạt động đúng như mong muốn.
Table uploads giờ đã có thêm 1 cột lưu đường dẫn của thumbnail.

App đã hiển thị thumbnail của các file đã upload. Khi bấm vào thumbnail, nó sẽ hiện ra file ảnh gốc.

V. Tính năng mở rộng của Intervention Image
Ngoài việc tạo thumbnail, thư viện Intervention Image còn cung cấp nhiều tính năng xử lý ảnh khác cực kỳ hữu ích cho ứng dụng thực tế
1. Resize theo kích thước mong muốn
$image->resize(800, 600); // Resize thành 800x600resize() sẽ bóp ảnh nhỏ lại đúng theo kích thước mong muốn, làm sai tỉ lệ (ratio) của ảnh. Nếu muốn thu nhỏ ảnh mà vẫn giữ nguyên tỉ lệ, cần phải dùng scale()
2. Scale giữ nguyên tỉ lệ ảnh
$image->scale(height: 300) // Giảm kích thước về chiều cao 300px3. Cắt ảnh
$image->crop(300, 300, 10, 10); // Cắt ảnh 300x300 từ góc trái (10,10)4. Thêm hiệu ứng ảnh
Ví dụ: làm mờ (blur), tăng độ sáng (brightness), độ tương phản (contrast)
$image->blur(5); // Làm mờ ảnh$image->brightness(-20); // Làm ảnh tối hơn$image->contrast(15); // Tăng tương phản5. Thêm watermark / text
Có thể ghi chữ lên ảnh hoặc chèn watermark:
$image->text('© ThuanBui.me', 10, 10, function ($font) { $font->size(24); $font->color('#ffffff');});VI. Lời kết
Trong phần 7 này, mình đã chia sẻ về cách tạo ảnh thumbnail với Intervention Image
-
Sử dụng Intervention Image để tạo ảnh thu nhỏ
-
Giảm tải giao diện, tiết kiệm băng thông
-
Tuỳ chọn lưu thumbnail vào cơ sở dữ liệu
Việc tạo ảnh thu nhỏ giúp hệ thống của bạn chạy nhanh hơn, gọn nhẹ hơn và tiết kiệm chi phí hơn khi hiển thị ảnh hàng loạt.
🔗 Mã nguồn
Tham khảo mã nguồn sử dụng trong [Phần 7] ở đây: https://github.com/10h30/laravel-file-upload-series/tree/part-7-thumbnail-intervention
🔜 Phần 8: Quản lý ảnh nâng cao với Spatie Media Library
Hiện tại, chúng ta đang xử lý upload, resize, lưu đường dẫn thủ công. Trong phần tiếp theo, mình sẽ chia sẻ về Spatie Media Library – một package mạnh mẽ cho việc quản lý file & ảnh trong Laravel.
-
Gắn file trực tiếp vào model
-
Tự động tạo và quản lý conversion (thumbnail, preview, v.v)
-
Tích hợp sẵn upload, delete, responsive images…
Hẹn gặp lại bạn trong phần 8 sẽ được ra lò vào ngày Thứ Hai, 19/05/2025!