PlusMagi's Blog By Pitt Phunsanit Backend PHP: การสตรีมไฟล์ดาวน์โหลดเพื่อประหยัด Memory (fpassthru)

PHP: การสตรีมไฟล์ดาวน์โหลดเพื่อประหยัด Memory (fpassthru)

ในฐานะ System Specialist และ Senior Architect เราทราบดีว่าการจัดการทรัพยากรของระบบเป็นสิ่งสำคัญที่สุดในการสร้างเว็บแอปพลิเคชันที่เสถียร โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับข้อมูลขนาดใหญ่ เช่น การดาวน์โหลดไฟล์รายงานหรือไฟล์สำรองข้อมูล (Backup File) ที่มีขนาดหลายร้อยเมกะไบต์ถึงกิกะไบต์

ปัญหาที่พบบ่อยคือการใช้ฟังก์ชัน PHP ทั่วไปที่โหลดเนื้อหาไฟล์ทั้งหมดเข้าสู่หน่วยความจำ (RAM) ก่อนส่งออก ซึ่งจะนำไปสู่ข้อผิดพลาดร้ายแรงอย่าง Memory Limit Exceeded หรือทำให้สคริปต์เกิด Time Out ได้อย่างง่ายดาย

💡 แนวคิดการสตรีมข้อมูล (Streaming Concept) ในมุมมองสถาปัตยกรรม

Streaming (การสตรีม) คือการประมวลผลข้อมูลแบบแบ่งส่วน (Chunking) โดยไม่จำเป็นต้องเก็บข้อมูลทั้งหมดไว้ในหน่วยความจำพร้อมกัน แนวคิดนี้เป็นหลักการสถาปัตยกรรมที่สำคัญ ไม่ว่าจะอยู่ในระดับฐานข้อมูล (เช่น การใช้ PostgreSQL Cursor เพื่อดึงผลลัพธ์ทีละชุด) หรือในระดับการส่งข้อมูลไฟล์

เป้าหมายของการสตรีมคือการให้ข้อมูลไหลผ่านระบบอย่างต่อเนื่อง (Stream) จากแหล่งกำเนิด (Source) ไปยังปลายทาง (Destination) โดยมีหน่วยความจำของแอปพลิเคชัน (PHP) ทำหน้าที่เป็นเพียงท่อส่งข้อมูลชั่วคราวเท่านั้น

🚀 การใช้ fpassthru ใน PHP เพื่อสตรีมไฟล์ดาวน์โหลด

สำหรับงานดาวน์โหลดไฟล์ขนาดใหญ่ใน PHP ฟังก์ชันที่เหมาะสมที่สุดคือ fpassthru() ฟังก์ชันนี้ถูกออกแบบมาโดยเฉพาะเพื่ออ่านเนื้อหาจาก File Pointer และส่งออกไปยัง Output Buffer ทันทีในรูปแบบของ Chunk โดยที่ PHP จะไม่ทำการโหลดเนื้อหาทั้งหมดของไฟล์เข้าสู่ตัวแปรหรือหน่วยความจำของสคริปต์เลยแม้แต่น้อย

ขั้นตอนการทำงานที่ถูกต้อง:

  1. เปิดไฟล์ (fopen): เปิดไฟล์ต้นทางด้วย fopen() เพื่อให้ได้ File Pointer
  2. กำหนด Header: ตั้งค่า HTTP Headers ให้ถูกต้องเพื่อบอกเบราว์เซอร์ว่ากำลังดาวน์โหลดไฟล์
  3. สตรีมข้อมูล (fpassthru): ใช้ fpassthru() เพื่ออ่านและส่งข้อมูลออกไปทีละส่วน
  4. ปิดไฟล์ (fclose): ปิด File Pointer ทันทีหลังการทำงานเสร็จสิ้น

ตัวอย่างโค้ด PHP (Streaming File Download)

PHP

<?php
// 1. กำหนดค่าไฟล์และชื่อดาวน์โหลด
$file_path = 'path/to/your/large_report.zip';
$download_filename = 'report_data.zip';

// 2. ตั้งค่า HTTP Headers
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $download_filename . '"');
header('Content-Length: ' . filesize($file_path));
header('Pragma: no-cache');

// 3. เปิด File Pointer
$handle = fopen($file_path, 'rb');

if ($handle) {
    // 4. สตรีมข้อมูลโดยใช้ fpassthru() 
    // ข้อมูลจะถูกส่งออกทันที ไม่เก็บใน RAM
    fpassthru($handle);
    
    // 5. ปิด File Pointer
    fclose($handle);
    
    // เคลียร์ Output Buffer เพื่อป้องกันการแสดงผลที่ไม่ต้องการ
    ob_flush();
    flush();
} else {
    die('Error: Cannot open file.');
}
?>

✅ บทสรุปสำหรับสถาปนิกระบบ

การเข้าใจหลักการ Streaming ไม่ได้จำกัดอยู่แค่การดาวน์โหลดไฟล์เท่านั้น แต่ยังเป็นหลักการสำคัญในการออกแบบระบบที่ต้องรับมือกับข้อมูลปริมาณมหาศาล ไม่ว่าจะเป็นการประมวลผลข้อมูลขนาดใหญ่ใน PostgreSQL โดยใช้ Cursor หรือการส่งข้อมูลผ่าน API การใช้เทคนิค Chunking เสมอจะช่วยให้แอปพลิเคชันของคุณมีความทนทาน (Robust) และมีประสิทธิภาพสูง (Scalable) อย่างแท้จริง ทำให้สามารถรองรับปริมาณผู้ใช้และข้อมูลที่เพิ่มขึ้นในอนาคตได้อย่างไร้รอยต่อ