- 1. สถาปัตยกรรม
- 2. สิ่งที่ต้องเตรียม
- 3. เปิด MinIO ด้วย Docker Compose
- 4. สร้างโปรเจกต์ Laravel
- 5. ตั้งค่าเชื่อมต่อ MinIO
- 6. สร้าง API Controller
- 7. กำหนด API Routes
- 8. ทดลองใช้งาน API
- 9. แบบฝึกหัดต่อยอด
- 10. ข้อควรระวังเมื่อใช้งานจริง
- สรุป
- เอกสารอ้างอิง
Workshop สำหรับพัฒนา REST API อัปโหลด ดาวน์โหลด และลบไฟล์ โดยใช้ MinIO เป็น S3-compatible object storage และ Laravel เป็น Backend
#1. สถาปัตยกรรม
flowchart LR U[Client / Postman] --> L[Laravel API] L -->|S3 API| M[MinIO Object Storage] L --> D[(Metadata Database - optional)]
MinIO เป็นระบบ Object Storage ที่รองรับ S3 API เหมาะกับการพัฒนาระบบเก็บรูปภาพ เอกสาร ไฟล์สำรอง และไฟล์ของแอปพลิเคชัน โดยสามารถติดตั้งภายในองค์กรได้
#2. สิ่งที่ต้องเตรียม
- Docker และ Docker Compose
- PHP, Composer และ Laravel (ใช้โปรเจกต์ Laravel ที่ติดตั้งแล้ว)
- โปรแกรมทดสอบ API เช่น Postman หรือ curl
#3. เปิด MinIO ด้วย Docker Compose
สร้างไฟล์ compose.yaml ในโฟลเดอร์แยกสำหรับ MinIO:
services:
minio:
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
command: server /data --console-address ":9001"
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: change-this-password-123
volumes:
- minio_data:/data
restart: unless-stopped
volumes:
minio_data:
แท็ก image เป็นตัวอย่างที่ระบุเวอร์ชันเพื่อให้ทดลองซ้ำได้ ควรตรวจสอบรุ่นที่ยังได้รับการสนับสนุนและเงื่อนไขการเผยแพร่ของ MinIO ก่อนใช้งานจริง และห้ามใช้รหัสผ่านตัวอย่างใน production
docker compose up -d
docker compose ps
เข้า MinIO Console ที่ http://localhost:9001 ด้วยบัญชีที่ตั้งไว้ จากนั้นสร้าง Bucket ชื่อ laravel-files โดยคงนโยบายเป็น private
#4. สร้างโปรเจกต์ Laravel
composer create-project laravel/laravel laravel-minio
cd laravel-minio
composer require league/flysystem-aws-s3-v3 "^3.0"
php artisan make:controller FileController
#5. ตั้งค่าเชื่อมต่อ MinIO
แก้ไข .env (กรณี Laravel ทำงานบนเครื่อง Host):
FILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=minioadmin
AWS_SECRET_ACCESS_KEY=change-this-password-123
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=laravel-files
AWS_ENDPOINT=http://127.0.0.1:9000
AWS_USE_PATH_STYLE_ENDPOINT=true
ตรวจสอบ config/filesystems.php ว่ามี disk s3 ดังนี้ (โปรเจกต์ Laravel รุ่นใหม่มักมีอยู่แล้ว):
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'bucket' => env('AWS_BUCKET'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', true),
'throw' => true,
],
หาก Laravel อยู่ใน Docker Compose network เดียวกับ MinIO ให้ใช้
AWS_ENDPOINT=http://minio:9000แทน127.0.0.1(และไม่ใช้localhostภายใน container)
php artisan config:clear
#6. สร้าง API Controller
ไฟล์ app/Http/Controllers/FileController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class FileController extends Controller
{
public function upload(Request $request)
{
$validated = $request->validate([
'file' => ['required', 'file', 'max:10240', 'mimes:jpg,jpeg,png,pdf,txt'],
]);
$file = $validated['file'];
$path = $file->storeAs(
'uploads',
Str::uuid() . '.' . $file->extension(),
's3'
);
return response()->json([
'message' => 'อัปโหลดสำเร็จ',
'path' => $path,
], 201);
}
public function download(string $name)
{
$path = 'uploads/' . $name;
abort_unless(Storage::disk('s3')->exists($path), 404);
return Storage::disk('s3')->download($path);
}
public function delete(string $name)
{
$path = 'uploads/' . $name;
abort_unless(Storage::disk('s3')->exists($path), 404);
Storage::disk('s3')->delete($path);
return response()->json(['message' => 'ลบไฟล์สำเร็จ']);
}
}
ตัวอย่างนี้กำหนดชื่อไฟล์ด้วย UUID เพื่อป้องกันชื่อชนกัน และ route รับเฉพาะชื่อไฟล์หนึ่งระดับ ไม่รับ path อิสระจากผู้ใช้
#7. กำหนด API Routes
สำหรับ Laravel 11+ หากยังไม่มี routes/api.php ให้เปิดใช้งาน API routing ก่อน:
php artisan install:api
แก้ไข routes/api.php:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FileController;
Route::post('/files', [FileController::class, 'upload']);
Route::get('/files/{name}', [FileController::class, 'download']);
Route::delete('/files/{name}', [FileController::class, 'delete']);
Routes นี้เปิดสาธารณะเพื่อสาธิตเฉพาะในเครื่องทดสอบ ก่อนเผยแพร่จริงต้องเพิ่ม authentication, authorization, rate limiting และการตรวจสอบสิทธิ์เจ้าของไฟล์
#8. ทดลองใช้งาน API
เปิด Laravel:
php artisan serve
อัปโหลด:
curl -X POST http://127.0.0.1:8000/api/files \
-F "file=@sample.pdf"
ดาวน์โหลด (นำชื่อไฟล์จาก path ที่ API ตอบกลับมา):
curl -OJ http://127.0.0.1:8000/api/files/FILE_UUID.pdf
ลบไฟล์:
curl -X DELETE http://127.0.0.1:8000/api/files/FILE_UUID.pdf
ตรวจสอบไฟล์ใน MinIO Console ที่ Bucket laravel-files ภายใต้โฟลเดอร์ uploads/
#9. แบบฝึกหัดต่อยอด
- เพิ่ม endpoint แสดงรายการไฟล์ด้วย
Storage::disk('s3')->files('uploads') - บันทึก metadata (owner, filename, MIME type, size) ลงฐานข้อมูล
- เพิ่ม Sanctum เพื่อยืนยันตัวตนและตรวจสอบสิทธิ์รายไฟล์
- เพิ่มการสร้างลิงก์ดาวน์โหลดชั่วคราว (presigned URL) โดยตรวจสอบ endpoint ที่ client เข้าถึงได้
- เพิ่ม automated API tests สำหรับไฟล์ที่ถูกต้อง ไฟล์เกินขนาด และการเข้าถึงโดยไม่มีสิทธิ์
#10. ข้อควรระวังเมื่อใช้งานจริง
- เปลี่ยน root credentials และใช้ access key ที่จำกัดสิทธิ์เฉพาะ bucket
- เปิด TLS/HTTPS และเก็บ secrets ใน secret manager
- ไม่เปิด MinIO Console หรือ S3 endpoint สู่สาธารณะโดยไม่ป้องกัน
- ตรวจสอบ MIME/file signatures และพิจารณาสแกนมัลแวร์สำหรับไฟล์จากผู้ใช้
- กำหนดนโยบาย backup, versioning, retention และ monitoring
- ไม่คืนค่า URL ของ bucket แบบ public โดยไม่ตั้งใจ เพราะ workshop นี้ใช้ private bucket
- การรัน Laravel และ MinIO คนละ network อาจทำให้ presigned URL ที่สร้างจาก internal hostname ใช้จาก browser ไม่ได้
#สรุป
Laravel ใช้ Flysystem S3 driver เชื่อมต่อ MinIO ผ่าน S3-compatible API ได้ ทำให้พัฒนา REST API สำหรับอัปโหลด ดาวน์โหลด และลบไฟล์ได้โดยไม่ผูกกับผู้ให้บริการ Cloud รายใดรายหนึ่ง และสามารถขยายไปสู่ระบบเอกสารหรือ media service ได้
#เอกสารอ้างอิง
- Laravel Filesystem: https://laravel.com/docs/filesystem
- MinIO Documentation: https://docs.min.io/
- Flysystem AWS S3 Adapter: https://flysystem.thephpleague.com/docs/adapter/aws-s3-v3/