Complete Part 7

This commit is contained in:
Thuan Bui
2025-05-09 18:51:25 +09:00
parent 7c1d409f69
commit c4288c40b9
7 changed files with 217 additions and 9 deletions
+1
View File
@@ -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.
+26 -4
View File
@@ -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();
+14 -2
View File
@@ -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
}
+1
View File
@@ -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"
Generated
+145 -1
View File
@@ -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",
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('uploads', function (Blueprint $table) {
$table->string('thumbnail')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('uploads', function (Blueprint $table) {
//
});
}
};
+2 -2
View File
@@ -53,7 +53,7 @@
<p class="text-sm text-gray-600">Original Filename:
{{ session("original_filenames")[$index] }}
</p>
<p class="text-sm text-gray-600">Stored Path: {{ $path }}</p>
{{-- <p class="text-sm text-gray-600">Stored Path: {{ $path }}</p> --}}
<img src="{{ $path }}" alt="Uploaded Image {{ $index + 1 }}"
class="mt-2 rounded max-w-full h-auto border">
</div>
@@ -70,7 +70,7 @@
<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->url }}" alt="{{ $upload->original_filename }}" width="50" height="50">
<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"