Pinion: Resumable File Upload อัปโหลดไฟล์ขนาดใหญ่โดยไม่ต้องเริ่มใหม่

โดย CyberMAN



📦 PHP / CodeIgniter 4 / Laravel

Pinion: Resumable File Upload
อัปโหลดไฟล์ขนาดใหญ่โดยไม่ต้องเริ่มใหม่

ใช้ tus protocol + tus-php library แก้ปัญหาการอัปโหลดไฟล์ที่ค้างกลางทาง ครั้งเดียวจบ

PHPCodeIgniter 4LaravelFile Uploadtus-php

🤔 ปัญหาที่นักพัฒนา PHP ทุกคนเคยเจอ

ลองนึกภาพสถานการณ์นี้ดู — ผู้ใช้งานของคุณพยายามอัปโหลดวิดีโอขนาด 2 GB ขึ้นไปยังเว็บแอปของคุณ รออยู่นาน 45 นาที แล้วอินเทอร์เน็ตก็ดับ หรือเบราว์เซอร์ค้าง ผลลัพธ์คือ? ต้องเริ่มอัปโหลดใหม่ตั้งแต่ต้น ซึ่งเป็นประสบการณ์ที่แย่มากสำหรับผู้ใช้

ปัญหานี้พบได้บ่อยมากในระบบที่ต้องรับไฟล์ขนาดใหญ่ เช่น ระบบ LMS ที่อัปโหลดวิดีโอบทเรียน, ระบบจัดการเอกสารของหน่วยงานภาครัฐ, หรือแอปพลิเคชัน Creative ที่รับไฟล์ RAW จากกล้อง

💡 แนวคิดหลัก: แทนที่จะอัปโหลดไฟล์ทั้งก้อนในครั้งเดียว ให้แบ่งไฟล์ออกเป็น chunks เล็ก ๆ แล้วส่งทีละก้อน ถ้าขาดกลางทาง ครั้งหน้าก็เริ่มต่อจากก้อนที่ค้างไว้ได้เลย

🔌 tus Protocol คืออะไร?

tus (อ่านว่า "tuːs") คือ open protocol สำหรับการอัปโหลดไฟล์แบบ resumable โดยเฉพาะ มันถูกออกแบบมาให้รองรับการหยุดกลางทาง ไม่ว่าจะโดยตั้งใจ (ผู้ใช้กด pause) หรือโดยอุบัติเหตุ (เน็ตหลุด, เบราว์เซอร์ปิด) แล้วสามารถกลับมาต่อจากจุดเดิมได้

จุดเด่นที่น่าสนใจอีกอย่างคือ คุณสามารถเริ่มอัปโหลดจากแล็ปท็อป แล้วมาต่อบนมือถือได้ เพราะ tus track ความคืบหน้าด้วย upload ID ที่ unique บน server ไม่ใช่บน device

📦 tus-php: Library ที่เราจะใช้

tus-php คือ pure PHP library ที่ implement tus protocol v1.0.0 ครบทั้ง server และ client โดยไม่ผูกติดกับ framework ใด ๆ (framework agnostic) จึงใช้กับทั้ง CodeIgniter 4 และ Laravel ได้สะดวก

ติดตั้งผ่าน Composer

terminal
composer require ankitpokhrel/tus-php

⚙️ หลักการทำงานแบบ Step by Step

01

Client แบ่งไฟล์เป็น Chunks

JavaScript ฝั่ง client แบ่งไฟล์ออกเป็นก้อนเล็ก ๆ (เช่น 5 MB ต่อก้อน) แล้วส่งทีละ request

02

Server เก็บ Offset

Server (tus-php) เก็บค่า offset ไว้ใน Redis หรือ Cache ว่าไฟล์อัปโหลดมาถึงไบต์ที่เท่าไรแล้ว

03

เกิดการขัดจังหวะ? ต่อได้เลย

Client ส่ง HEAD request มาถามว่า server รับข้อมูลถึงไหนแล้ว แล้วส่ง chunk ที่เหลือต่อทันที

04

ประกอบ Chunks เป็นไฟล์สมบูรณ์

เมื่อรับครบทุก chunk แล้ว server จะ merge เป็นไฟล์ต้นฉบับที่สมบูรณ์

🖥️ ฝั่ง Server: สร้าง TUS Endpoint

ไฟล์นี้ทำหน้าที่เป็น tus server — รับ request จาก client และจัดการ chunk ทั้งหมด ตัวอย่างด้านล่างใช้ Redis เป็น storage สำหรับเก็บ upload metadata

tus-server.php
<?php

require 'vendor/autoload.php';

// สร้าง TUS server โดยใช้ Redis เก็บ upload state
$server = new \TusPhp\Tus\Server('redis');

// กำหนด path ที่จะเก็บไฟล์
$server->setUploadDir('/var/www/uploads');

// กำหนดขนาด chunk สูงสุด (5 MB)
$server->setMaxUploadSize(5 * 1024 * 1024);

// ให้ server จัดการ request ทั้งหมด
$response = $server->serve();

$response->send();

exit(0); // จำเป็นต้อง exit เพื่อหยุด PHP process

ตั้งค่า Nginx ให้ชี้มาที่ server

nginx.conf
location /uploads {
    try_files $uri $uri/ /tus-server.php?$query_string;
}

🚀 ใช้กับ Laravel: ผ่าน Route

ใน Laravel ไม่จำเป็นต้องแก้ไข Nginx config ตรง ๆ — ให้กำหนด route ใน routes/web.php หรือ routes/api.php แทน

routes/api.php (Laravel)
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TusUploadController;

// รองรับ TUS protocol ทุก HTTP method
Route::any('/upload/{token?}', [TusUploadController::class, 'handle'])
    ->where('token', '[0-9a-zA-Z\-]+');
app/Http/Controllers/TusUploadController.php
<?php

namespace App\Http\Controllers;

use TusPhp\Tus\Server;

class TusUploadController extends Controller
{
    public function handle()
    {
        $server = new Server('redis');
        $server->setUploadDir(storage_path('app/uploads'));

        $response = $server->serve();
        $response->send();

        exit(0);
    }
}

🔥 ใช้กับ CodeIgniter 4

CI4 มี Router ที่ยืดหยุ่น สามารถ map ทุก HTTP method เข้า controller เดียวได้ง่าย

app/Config/Routes.php (CI4)
<?php

// รองรับทุก method สำหรับ TUS endpoint
$routes->add('upload/(:any)', 'TusUpload::handle/$1');
$routes->add('upload', 'TusUpload::handle');
app/Controllers/TusUpload.php (CI4)
<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use TusPhp\Tus\Server;

class TusUpload extends Controller
{
    public function handle()
    {
        $server = new Server('redis');
        $server->setUploadDir(WRITEPATH . 'uploads');

        $response = $server->serve();
        $response->send();

        exit(0);
    }
}

🌐 ฝั่ง Client: HTML Form + Uppy.js

tus-php ทำงานร่วมกับ JavaScript client library ได้หลายตัว ที่ได้รับความนิยมสูงสุดคือ Uppy.js ซึ่ง support tus protocol แบบ built-in และมี UI สวยงาม พร้อม progress bar

upload.blade.php / upload.php
<!-- โหลด Uppy CSS -->
<link rel="stylesheet" href="https://releases.transloadit.com/uppy/v3.3.1/uppy.min.css">

<div id="uppy-dashboard"></div>

<!-- โหลด Uppy JS -->
<script src="https://releases.transloadit.com/uppy/v3.3.1/uppy.min.js"></script>
<script>
  const uppy = new Uppy.Uppy()
    .use(Uppy.Dashboard, { target: '#uppy-dashboard', inline: true })
    .use(Uppy.Tus, {
      // ชี้ endpoint ไปที่ tus server
      endpoint: '/upload',
      // ขนาด chunk: 5 MB
      chunkSize: 5 * 1024 * 1024
    });

  uppy.on('complete', (result) => {
    console.log('อัปโหลดสำเร็จ:', result.successful);
  });
</script>

📊 เปรียบเทียบวิธีการอัปโหลดแบบต่าง ๆ

วิธีการResumableไฟล์ขนาดใหญ่ความยากติดตั้งเหมาะกับ
HTML Form อย่างเดียว❌ ไม่รองรับ❌ จำกัดมากง่ายมากไฟล์เล็ก < 10 MB
PHP chunked upload (manual)⚠️ ทำเองได้⚠️ พอใช้ได้ยากมากระบบที่ต้อง custom เยอะ
tus-php + tus protocol✅ รองรับเต็ม✅ ไม่จำกัดปานกลางไฟล์ใหญ่, production
Cloud Storage (S3, GCS)✅ รองรับ✅ ดีมากซับซ้อนScale ใหญ่, enterprise

🔧 ต้องการอะไรบ้าง?

📋 System Requirements:
PHP 7.4+ | Redis (แนะนำ) หรือ File-based cache | Composer | Nginx/Apache พร้อม URL Rewrite
Componentรายละเอียดหมายเหตุ
tus-phpLibrary หลักinstall via Composer
Redisเก็บ upload stateใช้ file cache แทนได้
Uppy.jsJS client สำหรับ browserหรือ tus-js-client
Writeable folderเก็บ chunk และไฟล์สุดท้ายchmod 775

🎯 ทำอะไรหลังอัปโหลดเสร็จ?

tus-php มี event listener ให้คุณดักจับตอนที่ไฟล์ upload ครบ 100% ได้ เหมาะสำหรับ trigger งานต่อเนื่อง เช่น แปลงวิดีโอ, สร้าง thumbnail, หรือบันทึกข้อมูลลงฐานข้อมูล

After Upload Hook
<?php

$server->event()->addListener(
    \TusPhp\Events\TusEvent::UPLOAD_COMPLETE,
    function (\TusPhp\Events\TusEvent $event) {
        $filePath = $event->getFile()->getFilePath();
        $fileName = $event->getFile()->getName();

        // บันทึกข้อมูลลงฐานข้อมูล
        \App\Models\MediaModel::create([
            'filename' => $fileName,
            'path'     => $filePath,
            'size'     => filesize($filePath),
            'status'   => 'ready',
        ]);
    }
);

📝 สรุป

การอัปโหลดไฟล์ขนาดใหญ่เป็นโจทย์ที่ดูเหมือนง่ายแต่มีรายละเอียดซ่อนอยู่มาก โดยเฉพาะเมื่อต้องรองรับผู้ใช้ที่มีอินเทอร์เน็ตไม่เสถียร tus protocol + tus-php library ช่วยแก้ปัญหานี้ได้อย่างสง่างาม โดยคุณไม่ต้องสร้าง chunking logic ขึ้นมาเอง

สิ่งที่ได้จากบทความนี้สถานะ
เข้าใจหลักการ Resumable Upload✅ ครอบคลุม
ติดตั้งและตั้งค่า tus-php server✅ ครอบคลุม
ใช้งานกับ Laravel✅ ครอบคลุม
ใช้งานกับ CodeIgniter 4✅ ครอบคลุม
ตั้งค่า Uppy.js client✅ ครอบคลุม
After-upload event hook✅ ครอบคลุม

ขั้นตอนต่อไปที่แนะนำคือ เพิ่ม authentication middleware เพื่อป้องกัน upload endpoint, กำหนด file type validation ก่อนประกอบ chunk, และตั้งค่า cleanup job สำหรับไฟล์ chunk ที่อัปโหลดค้างนาน ๆ



PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
ราคาสุดคุ้ม  
http://www.phpcodemania.com