Skip to content
Thuan Bui's Blog
Go back

File Upload trong Laravel - [Phần 6] Temporary URL và Upload lên MinIO (self-hosted S3)

Trong 5 phần đầu tiên của series Laravel File Upload, chúng ta đã lần lượt xây dựng tính năng upload file trong Laravel từ cơ bản đến nâng cao:

  1. Tìm hiểu cách upload file cơ bản

  2. Áp dụng validation và bảo mật

  3. Hỗ trợ upload nhiều file

  4. Lưu thông tin file vào database, hiển thị và xóa file

  5. Upload lên Amazon S3

Trong [Phần 6] này, chúng ta sẽ tìm hiểu thêm về

I. Tạo Temporary URL cho file trên S3

Trong phần trước, mình hướng dẫn cách cấu hình S3 Bucket ở chế độ public và sử dụng hàm getUrlAttribute khai báo trong model Upload.php để truy xuất Public URL của file

public function getUrlAttribute(): string
{
// Đảm bảo 's3' là tên disk chính xác được sử dụng để lưu trữ các file này.
$disk = 's3';
if ($this->filename) {
return Storage::disk($disk)->url($this->filename);
}
return ''; // Hoặc xử lý một cách thích hợp nếu tên tệp là null
}

1. Rủi ro khi dùng Public URL

Tuy nhiên, sử dụng Public URL sẽ gặp một số rủi ro:

Ví dụ thực tế:

Bạn upload một file PDF tài liệu khách hàng lên S3 và lấy link công khai để gửi cho họ. Sau đó:

Kết quả:

Điều này không chỉ làm lộ thông tin mà còn khiến bạn tốn chi phí rất cao nếu bị lạm dụng.

2. Temporary URL là giải pháp an toàn & linh hoạt

Để tránh các rủi ro trên, Laravel cho phép tạo Temporary URL (đường dẫn tạm thời) – chỉ hoạt động trong một khoảng thời gian nhất định.

Rất phù hợp cho:

3. Cách tạo Temporary URL trong Laravel

Laravel hỗ trợ sẵn method temporaryUrl() nếu bạn dùng S3 (hoặc các dịch vụ tương thích). Mình sẽ cập nhật lại hàm getUrlAttribute trong Model Upload.php như sau

public function getUrlAttribute(): string
{
// Đảm bảo 's3' là tên disk chính xác được sử dụng để lưu trữ các file này.
$disk = 's3';
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 ''; // Hoặc xử lý một cách thích hợp nếu tên tệp là null
}

→ Link chỉ có hiệu lực 5 phút, sau đó sẽ tự động vô hiệu hóa.

Đồng thời, minh sẽ vào Amazon S3, xóa policy của bucket upload để nó về lại chế độ private.

Ngoài ra, cần phải sửa trong file UploadController.php, dòng

$urlFilePath = Storage::disk($disk)->url($storedFilePath); // Trả về URL public của file

thành

$urlFilePath = Storage::disk($disk)->temporaryUrl($storedFilePath, now()->addMinutes(5)); // Trả về Temporary URL của file

Truy cập lại vào http://upload.test/upload, các file ảnh giờ sẽ có URL tương tư như sau

https://laravel-file-upload-series.s3.ap-southeast-1.amazonaws.com/uploads/Coolify%208.png?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA2LIPZ55FX65JVYU4%2F20250509%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20250509T031623Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Signature=a05326647621f098eafd35ee559e12b3102362752b3a0702647b170f8f1e5554

Sau 5’, thử truy cập lại vào file ảnh này, sẽ bị báo lỗi: This XML file does not appear to have any style information associated with it. The document tree is shown below do URL đã hết hiệu lực.

II. Khi nào dùng Public hay Private bucket?

Để hiển thị file trong Amazon S3, chúng ta có 2 cách

Vây khi nào thì nên để bucket ở chế độ công khai (public), và khi nào thì nên giữ riêng tư (private)?

1. Dùng Public Bucket khi

2. Dùng Private Bucket khi

III. Upload lên MinIO – Dịch vụ lưu trữ tương thích S3

Việc sử dụng Amazon S3 giúp bạn lưu trữ file an toàn và dễ dàng tích hợp vào Laravel. Tuy nhiên, chi phí băng thông (bandwidth) có thể trở thành một vấn đề lớn nếu:

Để kiểm soát chi phí tốt hơn mà vẫn giữ nguyên API tương thích với S3, bạn có thể sử dụng MinIO – một dịch vụ S3 self-hosted, mã nguồn mở, có thể cài đặt ngay trên server hoặc VPS cá nhân.

1. Cài đặt Minio

Tham khảo lại bài viết mình đã chia sẻ trước đây để biết cách cài đặt Minio, tạo Bucket và tạo Access Key

https://thuanbui.me/tao-object-storage-server-voi-minio-docker/

2. Cấu hình thông số trong Laravel

Tạo thêm disk mới trong config/filesystems.php:

'minio' => [
'driver' => 's3',
'key' => env('MINIO_KEY'),
'secret' => env('MINIO_SECRET'),
'region' => 'us-east-1',
'bucket' => env('MINIO_BUCKET'),
'endpoint' => env('MINIO_ENDPOINT'),
'use_path_style_endpoint' => true,
],

Bổ sung thông số của MINIO vào .env

MINIO_KEY=VFfxxxxxxxxr9Am
MINIO_SECRET=EEHetgd5xxxxxxxxxxWMr
MINIO_BUCKET=laravel-file-upload
MINIO_ENDPOINT=https://minio.thuanbui.me
MINIO_USE_SSL=true

3. Upload file lên Minio

Trong file Controller UploadController.php và model Upload.php cần thay đổi tất cả các phần khai báo $disk = 's3' thành $disk = 'minio' để hệ thống chuyển sang sử dụng disk minio.

Các file sau khi upload giờ sẽ được lưu trữ trên Minio thay vì Amazon S3.

TemporaryURL vẫn sẽ hoạt động khi lưu trữ file trên Minio storage.

Các tính năng khác như hiển thị danh sách file, xóa file đều hoạt động bình thường. Khi bấm Delete, file sẽ bị xóa khỏi thư mục lưu trữ trên S3.

IV. Lời kết

Trong [Phần 6] này, mình đã chia sẻ thêm một số kiến thức nâng cao liên quan đến S3:

Đây là những kiến thức quan trọng trong việc xây dựng hệ thống quản lý file thực tế – cần bảo mật, vừa tiết kiệm chi phí và linh hoạt khi triển khai.

🔗 Mã nguồn

Tham khảo mã nguồn sử dụng trong [Phần 6] ở đây: https://github.com/10h30/laravel-file-upload-series/tree/part-6-s3-temporary-url-minio

🔜 Phần 7: Tạo ảnh thu nhỏ với Intervention Image

Ở phần 7, chúng ta sẽ tìm hiểu tạo thêm thumbnail cho ảnh upload, sử dụng thư viện Intervention Image. Việc tạo thumbnail giúp giảm tải băng thông, tăng tốc độ hiển thị ảnh trong giao diện người dùng như: danh sách file, gallery ảnh, trang quản trị,… đồng thời giữ cho layout gọn gàng và tối ưu hơn.

Hẹn gặp lại ở [Phần 7] sẽ được ra lò vào tối Thứ Bảy - 17/05/2025!


Share this post on:

Previous Post
File Upload trong Laravel - [Phần 7] Tạo thumbnail (ảnh thu nhỏ) với Intervention Image
Next Post
Làm nổi bật mã nguồn trong Gutenberg với plugin Syntax-highlighting Code Block