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

PHP ในปี 2026: ทุกอย่างที่นักพัฒนาต้องรู้

โดย CyberMAN



📰 PHP Internals · June 17, 2026

PHP ในปี 2026:
ทุกอย่างที่นักพัฒนาต้องรู้

PHP 8.5 มาแล้ว — พร้อม Pipe Operator, Partial Function Application, Clone with Properties และอีกมาก มาสำรวจว่าสัปดาห์นี้มีอะไรเกิดขึ้นในโลก PHP Internals และจะกระทบกับ CodeIgniter 4 & Laravel อย่างไร

📅 17 มิถุนายน 2569⏱ อ่าน ~7 นาที🏷 PHP 8.5 · Laravel 13 · CI4

🚀เปิดฉาก: ทำไมปี 2026 ถึงน่าตื่นเต้นสำหรับ PHP?

ถ้าคุณยังคิดว่า PHP เป็นภาษา "โบราณ" ขอบอกเลยว่าคิดผิดมากครับ ปี 2026 เป็นช่วงเวลาที่ PHP กำลังเดินหน้าอย่างมั่นคงด้วย PHP 8.5 ที่ปล่อยออกมาช่วงปลายปี 2025 และนักพัฒนาส่วนใหญ่เพิ่งเริ่มนำมาใช้งานจริงในปีนี้

ในแต่ละสัปดาห์ ชุมชน PHP Internals จะมีการพูดคุย ถกเถียง และโหวต RFC เพื่อกำหนดทิศทางของภาษา บทความนี้จะสรุปสิ่งที่เกิดขึ้น พร้อมตัวอย่างโค้ดที่คุณเอาไปใช้ใน CodeIgniter 4 และ Laravel ได้เลย

🎯 เหมาะสำหรับผู้เริ่มต้นที่ใช้ CodeIgniter 4 หรือ Laravel และอยากตามทันฟีเจอร์ PHP ใหม่ ๆ ก่อนใคร

🔧ฟีเจอร์ที่ 1: Pipe Operator|>

Pipe Operator คือฟีเจอร์ที่นักพัฒนา PHP รอคอยมานาน ไอเดียเรียบง่ายมาก: แทนที่จะซ้อนฟังก์ชันหลาย ๆ ชั้นจนอ่านยาก คุณสามารถ "ส่งต่อ" ค่าจากซ้ายไปขวาได้เลย

ลองดูตัวอย่างแบบเดิมก่อนที่จะมี Pipe Operator:

before_pipe.php
// แบบเดิม — อ่านจากในออกนอก (สับสนมาก)
$result = array_sum(array_filter(array_map(
    fn($x) => $x * 2,
    [1, 2, 3, 4, 5]
), fn($x) => $x > 4));

และแบบใหม่ด้วย Pipe Operator — อ่านจากบนลงล่าง เข้าใจง่ายกว่ามาก:

after_pipe.php (PHP 8.5+)
// แบบใหม่ — อ่านจากซ้ายไปขวา ชัดเจนกว่า
$result = [1, 2, 3, 4, 5]
    |> array_map(fn($x) => $x * 2, ...)
    |> array_filter(..., fn($x) => $x > 4)
    |> array_sum(...);
💡 ใช้กับ Laravel ได้เลยใน Laravel Service หรือ Pipeline ที่มีการแปลงข้อมูลหลายขั้น Pipe Operator ช่วยให้โค้ดอ่านง่ายขึ้นมาก เหมาะกับ Data Transformation Layer ใน Clean Architecture เป็นพิเศษ

ฟีเจอร์ที่ 2: Partial Function Application (PFA)

นี่คือฟีเจอร์ที่ทำให้ Pipe Operator ทรงพลังขึ้นอีกเท่าตัว ปลายปี 2025 RFC นี้ผ่านการโหวต และจะกลายเป็นคู่หูที่แยกกันไม่ออกกับ Pipe Operator

PFA ช่วยให้คุณสร้าง "ฟังก์ชันสำเร็จรูปบางส่วน" โดยใส่ ? เป็น placeholder สำหรับพารามิเตอร์ที่จะส่งทีหลัง:

partial_function.php
// PFA — ล็อกพารามิเตอร์บางตัวไว้ก่อน ค่อยส่งที่เหลือทีหลัง
$double   = array_map(fn($x) => $x * 2, ?);
$moreThan4 = array_filter(?, fn($x) => $x > 4);

// รวมกับ Pipe Operator — อ่านลื่นมาก
$result = [1, 2, 3, 4, 5]
    |> $double(...)
    |> $moreThan4(...)
    |> array_sum(...);

var_dump($result); // int(24) — ผลลัพธ์: (4+6+8+10) = 28, filter >4 = 6+8+10=24

📦ฟีเจอร์ที่ 3: Clone with Properties

PHP 8.5 เพิ่มความสามารถในการ Clone object พร้อมเปลี่ยนค่า property ได้ในทีเดียว มีประโยชน์มากสำหรับ Value Object และ Immutable Data ใน Domain Layer

clone_with.php
// ตัวอย่าง: Value Object สำหรับ User ใน Laravel
class UserProfile
{
    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly string $role = 'user',
    ) {}
}

$original = new UserProfile('สมชาย', 'somchai@example.com');

// PHP 8.5: clone พร้อมเปลี่ยน property ได้เลย
$admin = clone $original with { role: 'admin' };

echo $original->role; // 'user'  — ของเดิมไม่เปลี่ยน
echo $admin->role;    // 'admin' — object ใหม่
⚠️ หมายเหตุSyntax ของ Clone with Properties อาจปรับเปลี่ยนเล็กน้อยตาม RFC ล่าสุด ควรตรวจสอบที่php.net/archive/2026ก่อน upgrade production เสมอ

🐛ฟีเจอร์ที่ 4: Backtrace สำหรับ Fatal Errors

เดิม PHP จะแสดง Fatal Error แบบกระชับ บอกแค่ไฟล์กับบรรทัด ทำให้ debug ยาก PHP 8.5 เพิ่ม stack trace ให้กับ Fatal Error เหมือนกับ Exception ทั่วไป

fatal_backtrace.txt — PHP 8.5 output
Fatal error: Maximum execution time of 1 second exceeded
         in example.php on line 6

#0 example.php(6): usleep(100000)
#1 example.php(12): slowOperation()
#2 example.php(20): processData()
#3 {main}

สำหรับโปรเจกต์ CI4 และ Laravel นี่หมายความว่า log ที่ได้จาก error handler จะมีข้อมูลครบขึ้นมาก ช่วยลดเวลา debug ได้อย่างชัดเจน

🌿Best Practices สำหรับ Laravel 13 ในปี 2026

Laravel 12 และ 13 เป็น version ที่ได้รับการ support อย่างเป็นทางการในปีนี้ โดย Laravel 13 รองรับ PHP 8.3–8.5 และจะได้รับ security update ไปถึงปี 2028

✅ ใช้ Form Request Validation แทน inline validation

app/Http/Requests/StoreProductRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreProductRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name'     => ['required', 'string', 'max:255'],
            'price'    => ['required', 'numeric', 'min:0'],
            'category' => ['required', 'exists:categories,id'],
        ];
    }

    public function messages(): array
    {
        return [
            'name.required'  => 'กรุณาระบุชื่อสินค้า',
            'price.min'      => 'ราคาต้องไม่ติดลบ',
            'category.exists' => 'หมวดหมู่ที่เลือกไม่มีในระบบ',
        ];
    }
}

✅ ใช้ Service Class แยกธุรกิจออกจาก Controller

app/Services/ProductService.php
namespace App\Services;

use App\Models\Product;
use App\Http\Requests\StoreProductRequest;

class ProductService
{
    public function store(StoreProductRequest $request): Product
    {
        return Product::create([
            'name'        => $request->validated('name'),
            'price'       => $request->validated('price'),
            'category_id' => $request->validated('category'),
            'created_by'  => auth()->id(),
        ]);
    }
}

// Controller บางเฉียบ — แค่เรียก Service
class ProductController
{
    public function store(
        StoreProductRequest $request,
        ProductService $service
    ) {
        $product = $service->store($request);

        return redirect()->route('products.show', $product)
                     ->with('success', 'เพิ่มสินค้าเรียบร้อยแล้ว');
    }
}

🔥สำหรับผู้ใช้ CodeIgniter 4: ใช้ PHP 8.5 ได้เลยไหม?

CI4 รองรับ PHP 8.1+ อยู่แล้ว และ PHP 8.5 backward compatible กับโค้ดเดิมเป็นส่วนใหญ่ Pipe Operator และ PFA เป็น syntax ใหม่ที่ไม่ได้เปลี่ยนแปลงของเดิม คุณสามารถ upgrade PHP เวอร์ชันได้โดยไม่ต้องแก้โค้ด CI4 เดิม แล้วค่อย ๆ นำ syntax ใหม่มาใช้

app/Models/ProductModel.php (CI4)
namespace App\Models;

use CodeIgniter\Model;

class ProductModel extends Model
{
    protected $table      = 'products';
    protected $allowedFields = ['name', 'price', 'category_id'];

    // PHP 8.5: ใช้ Pipe Operator ใน method ได้เลย
    public function getExpensiveProducts(float $minPrice): array
    {
        $items = $this->findAll();

        // แบบเดิม
        return array_values(array_filter(
            $items,
            fn($p) => $p['price'] >= $minPrice
        ));

        // แบบใหม่ (PHP 8.5) — อ่านง่ายกว่า
        // return $items
        //     |> array_filter(?, fn($p) => $p['price'] >= $minPrice)
        //     |> array_values(?);
    }
}

📊ตารางเปรียบเทียบ: PHP 8.4 vs 8.5 vs ที่กำลังจะมา

ฟีเจอร์PHP 8.4PHP 8.5กำลังจะมาประโยชน์หลัก
Pipe Operator |>✅ มีแล้วอ่านโค้ด transform ข้อมูลได้ง่ายขึ้น
Partial Function Application✅ มีแล้วสร้าง reusable function fragment ได้
Clone with Properties✅ มีแล้วImmutable Value Object สะอาดขึ้น
Fatal Error Backtrace✅ มีแล้วDebug production ได้ง่ายขึ้นมาก
Closures in Attributes✅ มีแล้วAttribute-driven metadata ยืดหยุ่นขึ้น
Property Hooks (get/set)✅ PHP 8.4แทน getter/setter ซ้ำซ้อนได้
Generics / Type System ขั้นสูงRFC กำลังพิจารณาType safety เทียบเท่า TypeScript
Laravel 13 + PHP 8.5✅ รองรับSecurity update ถึง 2028

🎯สรุป

สัปดาห์นี้ใน PHP Internals บอกเราได้ชัดว่า PHP กำลังวิ่งไปข้างหน้าอย่างไม่หยุด ฟีเจอร์อย่าง Pipe Operator และ Partial Function Application ไม่ใช่แค่ syntactic sugar แต่เป็นการเปลี่ยน paradigm การเขียนโค้ดของ PHP ให้ใกล้เคียงกับ functional programming มากขึ้น

สิ่งที่คุณควรทำหลังอ่านบทความนี้:

  • 🔄 อัปเกรดโปรเจกต์ CI4 / Laravel ให้ใช้ PHP 8.5
  • ✍️ ทดลองเขียน Pipe Operator กับโค้ด data transformation ที่มีอยู่
  • 📖 ติดตาม RFC ที่ php.net/archive/2026 อย่างสม่ำเสมอ
  • 🛡️ ตรวจสอบ ว่า Laravel ของคุณเป็น version 12 หรือ 13 เพื่อรับ security patch ถึงปี 2028

PHP ในปี 2026 ไม่ใช่ภาษาเดิมที่คุณรู้จักอีกต่อไปแล้วครับ — มันกำลัง mature ขึ้นทุกวัน

🚀 อยากพัฒนาทักษะ PHP & Laravel ให้เป็นมืออาชีพ?

ติดตาม PHP Code Mania สำหรับบทความภาษาไทยเชิงลึกเกี่ยวกับ PHP, CodeIgniter 4, Laravel และ Algorithmic Trading ทุกสัปดาห์

👉 ติดตามได้ที่
PHP 8.5Laravel 13CodeIgniter 4Pipe OperatorPartial Function ApplicationPHP InternalsPHP 2026Web Development


PHP CI MANIA - PHP Code Generator 

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