mirror of
https://github.com/10h30/laravel-file-upload-series.git
synced 2026-07-11 18:55:57 +09:00
Complete Part 7
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user