From c4288c40b9338dc01ec7c0833b2e6a857d23a8f8 Mon Sep 17 00:00:00 2001 From: Thuan Bui <9248622+10h30@users.noreply.github.com> Date: Fri, 9 May 2025 18:51:25 +0900 Subject: [PATCH] Complete Part 7 --- README.md | 1 + app/Http/Controllers/UploadController.php | 30 +++- app/Models/Upload.php | 16 +- composer.json | 1 + composer.lock | 146 +++++++++++++++++- ..._090856_add_thumbnail_to_uploads_table.php | 28 ++++ resources/views/upload.blade.php | 4 +- 7 files changed, 217 insertions(+), 9 deletions(-) create mode 100644 database/migrations/2025_05_09_090856_add_thumbnail_to_uploads_table.php diff --git a/README.md b/README.md index 45087b9..8216af7 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ | 4 | Hiển thị và xoá các file đã upload | [`part-4-manage-uploads`](https://github.com/10h30/laravel-file-upload-series/tree/part-4-manage-uploads) | | 5 | Upload file lên Amazon S3 | [`part-5-upload-to-s3`](https://github.com/10h30/laravel-file-upload-series/tree/part-5-upload-to-s3) | | 6 | Temporary URL & Upload lên MinIO | [`part-6-s3-temporary-url-minio`](https://github.com/10h30/laravel-file-upload-series/tree/part-6-s3-temporary-url-minio) | +| 7 | Create thumbnail with Intervetion Image | [`part-7-thumbnail-intervention`](https://github.com/10h30/laravel-file-upload-series/tree/part-7-thumbnail-intervention) | | | > 📖 Mỗi branch tương ứng với một phần trong series blog. Bạn có thể clone và chạy từng phần riêng biệt để dễ theo dõi. diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index 089a32c..a45ade7 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers; use App\Models\Upload; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; +use Intervention\Image\Drivers\Imagick\Driver; +use Intervention\Image\ImageManager; class UploadController extends Controller { @@ -39,6 +41,7 @@ class UploadController extends Controller $uploadedFiles = $request->file('files'); // Lấy array các đối tượng file đã upload $numberOfFiles = count($uploadedFiles); // Đếm số lượng file đã upload + $manager = new ImageManager(Driver::class); // Lặp qua từng file trong array $uploadedFiles foreach ($uploadedFiles as $file) { @@ -64,15 +67,30 @@ class UploadController extends Controller $counter++; } - // Lưu file bằng storeAs với tên file mới - $storedFilePath = $file->storeAs($directory, $finalFilename, $disk); // Trả về đường dẫn tương đối: 'uploads/ten_file_cuoi_cung.jpg' - $urlFilePath = Storage::disk($disk)->temporaryUrl($storedFilePath, now()->addMinutes(5)); // Trả về URL public của file - $storedFilePaths[] = $urlFilePath; // Lưu URL của từng file vào array $storedFilePath; // Thêm đường dẫn file đã lưu vào array $storedFilePaths + // Lưu file bằng storeAs với tên file mới và trả về đường dẫn tương đối: 'uploads/ten_file_cuoi_cung.jpg' + $storedFilePath = $file->storeAs($directory, $finalFilename, $disk); + + // Tạo thumbnail bằng Intervention Image + $thumbnail = $manager->read($file->getRealPath()) + ->resize(100, 100); // Resize to fit 100x100, maintaining aspect ratio + + // Đườ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()); + + // Trả về Temporary URL của file + $urlFilePath = Storage::disk($disk)->temporaryUrl($thumbnailStoragePath, now()->addMinutes(5)); + + // Thêm đường dẫn file đã lưu vào array $storedFilePaths + $storedFilePaths[] = $urlFilePath; // Tạo bản ghi mới trong table uploads của database Upload::create([ 'filename' => $storedFilePath, 'original_filename' => $originalFilename, + 'thumbnail' => $thumbnailStoragePath, ]); } @@ -95,6 +113,10 @@ class UploadController extends Controller Storage::disk($disk)->delete($upload->filename); } + if (Storage::disk($disk)->exists($upload->thumbnail)) { + Storage::disk($disk)->delete($upload->thumbnail); + } + // Xoá bản ghi tương ứng trong database $upload->delete(); diff --git a/app/Models/Upload.php b/app/Models/Upload.php index 1a2371d..3af39a2 100644 --- a/app/Models/Upload.php +++ b/app/Models/Upload.php @@ -9,7 +9,8 @@ class Upload extends Model { protected $fillable = [ 'filename', - 'original_filename' + 'original_filename', + 'thumbnail', ]; public function getUrlAttribute(): string @@ -19,7 +20,18 @@ class Upload extends Model if ($this->filename) { // Thao tác này tạo ra một URL tạm thời mới mỗi khi thuộc tính 'url' được truy cập. // URL sẽ có hiệu lực trong 5 phút kể từ thời điểm nó được tạo. - return Storage::disk($disk)->temporaryUrl($this->filename, now()->addMinutes(5)); + return Storage::disk($disk)->temporaryUrl($this->filename, now()->addMinutes(5)); // Fixed typo: $this-> to $this->filename + } + return ''; // Hoặc xử lý một cách thích hợp nếu tên tệp là null + } + + // 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 } diff --git a/composer.json b/composer.json index 3ac9320..322db9b 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "license": "MIT", "require": { "php": "^8.2", + "intervention/image": "^3.11", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", "league/flysystem-aws-s3-v3": "^3.0" diff --git a/composer.lock b/composer.lock index ed3f476..8d00e50 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ab138229f849545a43d8225fa9bb27fc", + "content-hash": "c0294f265e4412b55b1e7491761d059f", "packages": [ { "name": "aws/aws-crt-php", @@ -1205,6 +1205,150 @@ ], "time": "2025-02-03T10:55:03+00:00" }, + { + "name": "intervention/gif", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "5999eac6a39aa760fb803bc809e8909ee67b451a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/5999eac6a39aa760fb803bc809e8909ee67b451a", + "reference": "5999eac6a39aa760fb803bc809e8909ee67b451a", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Native PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/4.2.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2025-03-29T07:46:21+00:00" + }, + { + "name": "intervention/image", + "version": "3.11.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "ebbb711871fb261c064cf4c422f5f3c124fe1842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/ebbb711871fb261c064cf4c422f5f3c124fe1842", + "reference": "ebbb711871fb261c064cf4c422f5f3c124fe1842", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "intervention/gif": "^4.2", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "suggest": { + "ext-exif": "Recommended to be able to read EXIF data properly." + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "PHP image manipulation", + "homepage": "https://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "resize", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/3.11.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2025-02-27T13:08:55+00:00" + }, { "name": "laravel/framework", "version": "v12.12.0", diff --git a/database/migrations/2025_05_09_090856_add_thumbnail_to_uploads_table.php b/database/migrations/2025_05_09_090856_add_thumbnail_to_uploads_table.php new file mode 100644 index 0000000..3742399 --- /dev/null +++ b/database/migrations/2025_05_09_090856_add_thumbnail_to_uploads_table.php @@ -0,0 +1,28 @@ +string('thumbnail')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('uploads', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/views/upload.blade.php b/resources/views/upload.blade.php index 6ecc989..58f879c 100644 --- a/resources/views/upload.blade.php +++ b/resources/views/upload.blade.php @@ -53,7 +53,7 @@

Original Filename: {{ session("original_filenames")[$index] }}

-

Stored Path: {{ $path }}

+ {{--

Stored Path: {{ $path }}

--}} Uploaded Image {{ $index + 1 }} @@ -70,7 +70,7 @@