ข้ามไปที่เนื้อหาหลัก

เทคนิคการดาวน์โหลด Excel 200,000 แถว ด้วย PHP 8 และ CodeIgniter 3 (เสถียร 100% RAM ไม่เต็ม)

เทคนิคการดาวน์โหลด Excel 200,000 แถว ด้วย PHP 8 และ CodeIgniter 3 (เสถียร 100% RAM ไม่เต็ม)

การทำรายงานส่งออกไฟล์ Excel ขนาดใหญ่ (.xlsx) บนเว็บแอปพลิเคชัน มักประสบปัญหาหลักสองอย่างคือ หน่วยความจำเต็ม (Out of Memory) และ เว็บค้างจนหมดเวลา (Execution Timeout) โดยเฉพาะถ้าเราใช้ Library ยอดนิยมอย่าง PhpSpreadsheet แบบปกติ เมื่อมีข้อมูลเกินแสนแถว ระบบก็พร้อมที่จะล่มได้ทุกเมื่อ

บทความนี้จะมาแบ่งปันแนวทางแก้ไขปัญหาด้วยการผสานพลังของ PHP 8, OpenSpout (Streaming Library) และการทำ Unbuffered Query ใน CodeIgniter 3 ที่จะช่วยให้ระบบใช้ RAM คงที่เพียง 15-20 MB ตลอดระยะเวลาการดาวน์โหลดข้อมูลสองแสนแถวครับ

1. แนวคิดหลัก: Streaming & Unbuffered Query

หากต้องการประหยัดหน่วยความจำ เราจำเป็นต้องหลีกเลี่ยงการนำข้อมูลทั้งหมดเข้าสู่ RAM ของ PHP ในคราวเดียว:

  • Unbuffered Query: จะดึงข้อมูลจากฐานข้อมูลขึ้นมาประมวลผลทีละ 1 แถวแทนการโหลดขึ้นมาเป็น Array ขนาดใหญ่
  • Streaming Writer: OpenSpout จะทำการเขียนข้อมูลลงใน Temp File ทันที และสตรีมข้อมูลผลลัพธ์ยิงตรงไปที่หน้าจอผู้ใช้ โดยไม่ต้องเก็บข้อมูลทุกแถวไว้ใน RAM

2. ขั้นตอนการตั้งค่าใน CodeIgniter 3

ขั้นตอนที่ 2.1: เปิดใช้งาน Composer Autoload

เปิดไฟล์ application/config/config.php และตรวจสอบให้แน่ใจว่าได้เปิดใช้งาน Autoload ของ Composer แล้ว:

$config['composer_autoload'] = TRUE;

ขั้นตอนที่ 2.2: ติดตั้ง OpenSpout ผ่าน Composer

รันคำสั่งนี้ที่ Root Directory ของโปรเจกต์คุณผ่าน Terminal:

composer require openspout/openspout

3. เขียนโค้ดใน Controller

สร้างไฟล์ Controller ที่ application/controllers/ExcelExport.php:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

use OpenSpout\Writer\XLSX\Writer;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Cell;

class ExcelExport extends CI_Controller {

    public function __construct() {
        parent::__construct();
        $this->load->database();
    }

    public function download() {
        // 1. ปลดล็อกข้อจำกัดของระบบชั่วคราว
        set_time_limit(0); 
        ini_set('memory_limit', '128M');

        // 2. เตรียม Writer ของ OpenSpout สตรีมไปเบราว์เซอร์
        $writer = new Writer();
        $fileName = 'report_' . date('Ymd_His') . '.xlsx';
        $writer->openToBrowser($fileName);

        // 3. เขียนแถวหัวตาราง (Header)
        $headers = ['รหัส', 'ชื่อลูกค้า', 'อีเมล', 'เบอร์โทรศัพท์', 'คะแนนสะสม'];
        $headerCells = [];
        foreach ($headers as $title) {
            $headerCells[] = Cell::fromValue($title);
        }
        $writer->addRow(new Row($headerCells));

        // 4. ใช้ unbuffered_row('array') เพื่อประหยัด RAM
        $sql = "SELECT id, name, email, phone, points FROM customers";
        $query = $this->db->query($sql);

        while ($row = $query->unbuffered_row('array')) {
            $cells = [
                Cell::fromValue($row['id']),
                Cell::fromValue($row['name']),
                Cell::fromValue($row['email']),
                Cell::fromValue($row['phone']),
                Cell::fromValue((int)$row['points']),
            ];
            $writer->addRow(new Row($cells));
        }

        // 5. ปิดการเชื่อมต่อเขียนไฟล์
        $writer->close();
        exit;
    }
}

4. ข้อควรระวังในการดาวน์โหลดข้อมูลมหาศาล

⚠️ ระวังเรื่อง Output อื่น ๆ ก่อนการสตรีมไฟล์

ตรวจสอบให้แน่ใจว่าไม่มีคำสั่ง echo, print หรือข้อความ Error/Warning แสดงผลก่อนหรือระหว่างกระบวนการดาวน์โหลด เนื่องจากจะทำให้ตัวเข้ารหัสไฟล์ Excel ผิดพลาดและดาวน์โหลดไฟล์ชำรุดเสียหายเปิดใช้งานไม่ได้

💡 การตกแต่งสไตล์ (Styles)

การแต่งสีหรือกำหนดฟอนต์ที่หลากหลายในทุกเซลล์ข้อมูลจำทำให้การสร้างไฟล์ XML ภายหลังทำงานช้าลงอย่างมาก แนะนำให้ตกแต่งสีสันเฉพาะแถวหัวข้อ (Header) และปล่อยให้แถวข้อมูลที่เหลือมีรูปแบบเป็นค่าตั้งต้น

สรุปผลลัพธ์

ด้วยโครงสร้างของ PHP 8 ที่ประมวลผลได้ดีขึ้น ประกอบกับการดึงข้อมูลทีละแถวด้วย unbuffered_row() ใน CI3 และการสตรีมเขียนลงดิสก์ด้วย OpenSpout จะทำให้การดึงข้อมูล 200,000 แถว กลายเป็นเรื่องง่ายและใช้ทรัพยากรเครื่องเซิร์ฟเวอร์ต่ำมากอย่างไม่น่าเชื่อครับ!




PHP CI MANIA - PHP Code Generator 

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

ความคิดเห็น

ค้นหาบล็อกนี้

บทความเดือนล่าสุด