Đây là bài viết Phần 7 nằm trong series: Laravel File Upload

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'Code language: Nginx (nginx)

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

imagick
mbstringCode language: Nginx (nginx)

2. Cài đặt Intervention Image

Cài đặt bằng composer

composer require intervention/imageCode language: JavaScript (javascript)

III. 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);Code language: PHP (php)

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());Code language: PHP (php)

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));Code language: PHP (php)

Ả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=uploadsCode language: Nginx (nginx)

Chỉnh sửa file migration

    public function up(): void
    {
        Schema::table('uploads', function (Blueprint $table) {
            $table->string('thumbnail')->nullable();
        });
    }Code language: CSS (css)

Chạy migration để áp dụng những thay đổi cho database

php artisan migrateCode language: Nginx (nginx)

2. Cập nhật model Upload

Trong app/Models/Upload.php, thêm thumbnail vào $fillable:

protected $fillable = [
    'filename',
    'original_filename',
    'thumbnail',
];Code language: PHP (php)

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,'
            ]);Code language: PHP (php)

Đồ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);
        }Code language: PHP (php)

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
    }Code language: PHP (php)

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>
    @endif
Code language: HTML, XML (xml)

Thử 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 800x600Code language: PHP (php)

resize() 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 300pxCode language: PHP (php)

3. Cắt ảnh

$image->crop(300, 300, 10, 10); // Cắt ảnh 300x300 từ góc trái (10,10)Code language: PHP (php)

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ảnCode language: PHP (php)

5. 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');
});Code language: PHP (php)

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!

Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất
Phản hồi nội tuyến
Xem tất cả bình luận