รู้จักกับ Built-in Modules และหัวใจของ File System
ในการเขียนโปรแกรมด้วย Node.js (สภาพแวดล้อมที่ช่วยให้รัน JavaScript บนเครื่องคอมพิวเตอร์ได้) เรามักจะต้องทำงานกับไฟล์บนเครื่องอยู่เสมอ ไม่ว่าจะเป็นการอ่านค่าจากไฟล์ตั้งค่า การเขียนบันทึกข้อมูล หรือการสร้างโฟลเดอร์ใหม่เพื่อจัดระเบียบโปรเจกต์ ซึ่ง Built-in Modules (ชุดคำสั่งสำเร็จรูปที่ติดมากับ Node.js โดยไม่ต้องติดตั้งเพิ่ม) คือตัวช่วยสำคัญที่ทำให้งานเหล่านี้ง่ายขึ้นมาก
หนึ่งในโมดูลที่โปรแกรมเมอร์ต้องใช้บ่อยที่สุดคือ File System หรือที่เราเรียกสั้นๆ ว่า fs module (ชุดคำสั่งจัดการระบบไฟล์) เปรียบเสมือนเลขาที่คอยจัดการเอกสารในตู้เก็บของให้เรา ลองจินตนาการว่าถ้าเราต้องสั่งให้คอมพิวเตอร์สร้างไฟล์ด้วยตัวเองโดยไม่มีตัวช่วย เราคงต้องเขียนคำสั่งที่ซับซ้อนมาก แต่ fs module เข้ามาจัดการเรื่องนี้ให้เราด้วยคำสั่งเพียงไม่กี่บรรทัด
การเข้าใจการทำงานของ fs module ไม่ใช่แค่เรื่องของการจัดการไฟล์ แต่คือการเข้าใจว่าโปรแกรมของเราจะ โต้ตอบ กับโลกภายนอกได้อย่างไร ไม่ว่าจะเป็นการอ่านข้อมูลที่ผู้ใช้ส่งมา หรือการเก็บข้อมูลที่ประมวลผลเสร็จแล้วลงในดิสก์ (อุปกรณ์เก็บข้อมูลถาวร) การเรียนรู้เรื่องนี้จึงเป็นจุดเริ่มต้นสำคัญสำหรับคนที่อยากเป็น Backend Developer (นักพัฒนาส่วนหลังของระบบที่ทำงานบนเซิร์ฟเวอร์) ที่เก่งและทำงานได้จริง
ความแตกต่างระหว่าง Synchronous และ Asynchronous
ก่อนจะเริ่มเขียนโค้ด เราต้องเข้าใจแนวคิดเรื่องการทำงานสองรูปแบบใน Node.js ก่อน คือ Synchronous (การทำงานแบบทำทีละขั้นตอนจนจบ) และ Asynchronous (การทำงานแบบไม่รอให้งานเดิมเสร็จก็เริ่มงานใหม่ได้) ซึ่ง fs module มีคำสั่งรองรับทั้งสองแบบเพื่อให้เราเลือกใช้ตามความเหมาะสมของงาน
ลองนึกภาพการทำอาหาร ถ้าเป็นแบบ Synchronous ก็เหมือนเรายืนหั่นผักให้เสร็จก่อน แล้วค่อยไปตั้งหม้อต้มน้ำ ซึ่งถ้าผักเยอะเราก็ต้องยืนรอนานจนไม่ได้ทำอย่างอื่น แต่ถ้าเป็นแบบ Asynchronous ก็เหมือนเราเปิดเตาต้มน้ำทิ้งไว้ แล้วค่อยหั่นผักระหว่างรอให้น้ำเดือด วิธีนี้ช่วยให้โปรแกรมของเราไม่ ค้าง หรือหยุดชะงักระหว่างที่กำลังรออ่านไฟล์ขนาดใหญ่
สำหรับการเขียนโปรแกรมทั่วไป การใช้แบบ Asynchronous เป็นแนวทางที่แนะนำมากกว่า เพราะช่วยให้โปรแกรมรองรับผู้ใช้งานได้หลายคนพร้อมกันโดยไม่สะดุด แต่ในบางกรณี เช่น การอ่านไฟล์ตั้งค่าตอนเริ่มเปิดโปรแกรมครั้งแรก การใช้แบบ Synchronous ก็อาจจะดูเข้าใจง่ายและเหมาะสมกว่า ทั้งนี้ขึ้นอยู่กับว่าเราต้องการให้โปรแกรมทำงานเร็วแค่ไหนและในสถานการณ์ใด
const fs = require('fs');
// แบบ Synchronous: โปรแกรมจะหยุดรอจนกว่าจะอ่านไฟล์เสร็จ
try {
const data = fs.readFileSync('test.txt', 'utf8');
console.log('เนื้อหาในไฟล์:', data);
} catch (err) {
console.error('อ่านไฟล์ไม่สำเร็จ:', err);
}
// แบบ Asynchronous: โปรแกรมจะทำงานอื่นต่อทันที ไม่ต้องรอ
fs.readFile('test.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('เนื้อหาในไฟล์ (Async):', data);
});
ในโค้ดชุดแรก เราเรียกใช้ require('fs') เพื่อนำเข้าโมดูลมาใช้งาน ส่วน readFileSync จะเป็นการอ่านไฟล์แบบรอให้เสร็จก่อนถึงไปบรรทัดถัดไป สำหรับ readFile จะรับพารามิเตอร์ตัวสุดท้ายเป็น Callback Function (ฟังก์ชันที่จะทำงานเมื่อได้รับผลลัพธ์แล้ว) เพื่อจัดการข้อมูลหลังจากอ่านเสร็จ
ผลลัพธ์ที่ได้จากการรันคือ ข้อความที่อยู่ในไฟล์ test.txt จะถูกแสดงผลออกมาที่หน้าจอคอนโซล หากไม่มีไฟล์ชื่อนั้นอยู่ แบบ Synchronous จะกระโดดไปที่บล็อก catch เพื่อบอกข้อผิดพลาด ส่วนแบบ Asynchronous จะคืนค่าความผิดพลาดผ่านตัวแปร err ในฟังก์ชันแทน
การสร้างและเขียนไฟล์ด้วย fs module
การสร้างไฟล์ใหม่ใน Node.js นั้นสะดวกมาก เราสามารถใช้คำสั่ง writeFile เพื่อเขียนข้อความลงไปในไฟล์ได้เลย หากไฟล์นั้นไม่มีอยู่จริง ระบบก็จะสร้างไฟล์ใหม่ให้โดยอัตโนมัติ ซึ่งเหมาะมากสำหรับการทำระบบเก็บ Log (บันทึกการทำงานของโปรแกรม) หรือบันทึกข้อมูลการสมัครสมาชิกของผู้ใช้
หัวใจสำคัญของการเขียนไฟล์คือการตรวจสอบให้แน่ใจว่าเราไม่ได้ไปทับไฟล์สำคัญของระบบ การระบุชื่อไฟล์ต้องชัดเจนและควรมีการจัดการข้อผิดพลาดทุกครั้ง เพราะบางครั้งเราอาจไม่มีสิทธิ์ (Permission) ในการเขียนไฟล์ลงในโฟลเดอร์นั้นๆ ซึ่งเป็นเรื่องที่โปรแกรมเมอร์มือใหม่มักจะลืมตรวจสอบจนทำให้โปรแกรมพังได้ง่ายๆ
นอกจากนี้เรายังสามารถใช้ appendFile ในกรณีที่เราต้องการเพิ่มเนื้อหาต่อท้ายไฟล์เดิม แทนที่จะเขียนทับไฟล์เก่าทั้งหมด เหมาะสำหรับการเพิ่มข้อมูลลงในไฟล์ประวัติการใช้งาน (History) อย่างต่อเนื่องโดยไม่ลบข้อมูลเก่าที่มีอยู่เดิมทิ้งไป
const fs = require('fs');
// สร้างไฟล์ใหม่และเขียนข้อความลงไป
fs.writeFile('hello.txt', 'สวัสดีชาวโลก', (err) => {
if (err) throw err;
console.log('บันทึกไฟล์เรียบร้อย');
});
// เพิ่มข้อความต่อท้ายไฟล์เดิม
fs.appendFile('hello.txt', '\nเพิ่มบรรทัดใหม่', (err) => {
if (err) throw err;
console.log('เพิ่มข้อมูลสำเร็จ');
});
บรรทัด writeFile จะรับชื่อไฟล์และข้อความที่จะบันทึกเป็นค่าเริ่มต้น ส่วน appendFile จะทำงานคล้ายกันแต่เป็นการเติมข้อความต่อท้าย สัญลักษณ์ \n คือคำสั่งขึ้นบรรทัดใหม่ในทางโปรแกรมมิ่ง เพื่อให้ข้อมูลดูเป็นระเบียบเมื่อเปิดไฟล์อ่าน
ผลลัพธ์ที่ควรเห็นคือ จะมีไฟล์ชื่อ hello.txt ปรากฏขึ้นในโฟลเดอร์โปรเจกต์ของคุณ เมื่อเปิดไฟล์ดูจะพบข้อความว่า "สวัสดีชาวโลก" และในบรรทัดถัดมาจะมีคำว่า "เพิ่มบรรทัดใหม่" ปรากฏอยู่ด้วย
การจัดการโฟลเดอร์และลบไฟล์
นอกจากจัดการไฟล์แล้ว เรายังสามารถจัดการโฟลเดอร์ได้ด้วยการใช้ mkdir (ย่อมาจาก Make Directory) เพื่อสร้างโฟลเดอร์ใหม่ และใช้ unlink เพื่อลบไฟล์ทิ้ง ซึ่งเป็นทักษะพื้นฐานในการทำระบบจัดการไฟล์ส่วนตัวหรือระบบอัปโหลดรูปภาพที่ต้องมีการจัดเก็บไฟล์เป็นหมวดหมู่
การลบไฟล์เป็นเรื่องที่ต้องระมัดระวังมากที่สุด เพราะคำสั่ง unlink จะลบไฟล์ทิ้งไปเลยโดยไม่ผ่านถังขยะเหมือนการลบใน Windows หรือ Mac ดังนั้นก่อนเขียนคำสั่งลบ ควรมีการตรวจสอบเงื่อนไขให้แน่ใจเสมอว่าไฟล์นั้นมีอยู่จริงและเป็นไฟล์ที่เราต้องการลบจริงๆ
สำหรับการลบโฟลเดอร์ เราจะใช้ rmdir แต่มีข้อแม้ว่าโฟลเดอร์นั้นต้องว่างเปล่าก่อนถึงจะลบได้ หากในโฟลเดอร์ยังมีไฟล์อยู่เราต้องลบไฟล์ข้างในออกให้หมดก่อนเสมอ เป็นขั้นตอนที่ดูเหมือนยุ่งยากแต่ก็ปลอดภัยเพื่อป้องกันการลบข้อมูลสำคัญทิ้งไปโดยไม่ได้ตั้งใจ
const fs = require('fs');
// สร้างโฟลเดอร์ใหม่
fs.mkdir('data', (err) => {
if (err) console.log('โฟลเดอร์นี้มีอยู่แล้ว');
});
// ลบไฟล์ทิ้ง
fs.unlink('hello.txt', (err) => {
if (err) throw err;
console.log('ลบไฟล์เรียบร้อย');
});
คำสั่ง mkdir จะพยายามสร้างโฟลเดอร์ชื่อ data หากมีโฟลเดอร์ชื่อนี้อยู่แล้วโปรแกรมจะส่งค่า err กลับมา ส่วน unlink จะทำการลบไฟล์ hello.txt ออกไปจากระบบถาวรทันทีที่รันคำสั่งสำเร็จ
ผลลัพธ์ที่ได้คือ โฟลเดอร์ที่ชื่อว่า data จะปรากฏขึ้นในไดเรกทอรีปัจจุบัน และไฟล์ hello.txt จะหายไปจากเครื่องของคุณหลังจากที่รันโค้ดส่วนนี้เสร็จสิ้น
ก้าวไปอีกขั้นด้วย Promises API
ในเวอร์ชันใหม่ๆ ของ Node.js เราสามารถใช้ Promises API (รูปแบบการเขียนโปรแกรม Asynchronous ที่อ่านง่ายและจัดการข้อผิดพลาดได้ดีขึ้น) แทนการใช้ Callback แบบเดิมได้ วิธีนี้จะช่วยให้โค้ดของเราดูสะอาดตาและไม่เกิดอาการ Callback Hell (การเขียนฟังก์ชันซ้อนกันหลายชั้นจนโค้ดอ่านยาก)
การใช้ Promises ช่วยให้เราเขียนโค้ดแบบบรรทัดต่อบรรทัดได้เหมือนกับ Synchronous แต่ยังคงความเป็น Asynchronous เอาไว้ ทำให้โปรแกรมทำงานได้รวดเร็วเหมือนเดิม เพียงแค่เพิ่ม /promises ต่อท้ายชื่อโมดูล fs เราก็สามารถใช้คำสั่ง async/await (คำสั่งที่ช่วยให้เขียนโค้ด Asynchronous ให้ดูเหมือนงานปกติ) ได้ทันที
สำหรับโปรแกรมเมอร์ยุคใหม่ การหัดใช้ Promises เป็นเรื่องที่คุ้มค่ามาก เพราะในเฟรมเวิร์กส่วนใหญ่ที่ใช้ในงานจริง เช่น Express.js (เครื่องมือช่วยสร้างเว็บเซิร์ฟเวอร์) ต่างก็รองรับการทำงานด้วยวิธีนี้เป็นหลัก การปรับตัวมาใช้ตั้งแต่วันนี้จะช่วยให้คุณทำงานร่วมกับทีมได้ง่ายขึ้นในอนาคต
const fs = require('fs').promises;
// ใช้ async/await เพื่อความสะอาดของโค้ด
async function manageFile() {
try {
await fs.writeFile('test.txt', 'ข้อมูลใหม่');
const data = await fs.readFile('test.txt', 'utf8');
console.log('อ่านได้แล้ว:', data);
} catch (err) {
console.error('เกิดข้อผิดพลาด:', err);
}
}
manageFile();
ในโค้ดนี้เราใช้ require('fs').promises เพื่อดึงความสามารถของ Promises มาใช้ คำสั่ง await จะช่วยให้โปรแกรมรอให้คำสั่งนั้นทำงานเสร็จก่อนค่อยไปบรรทัดถัดไป โดยที่ยังทำงานแบบไม่บล็อกโปรแกรมหลักอยู่
ผลลัพธ์ที่ควรเห็นคือ โปรแกรมจะสร้างไฟล์ test.txt บันทึกข้อมูล และอ่านข้อมูลนั้นออกมาแสดงผลบนหน้าจอตามลำดับขั้นตอนอย่างสวยงาม หากเกิดข้อผิดพลาด โปรแกรมจะกระโดดไปที่ catch เพื่อบอกสาเหตุให้เราทราบทันที
สรุป: นำความรู้ไปใช้ในโปรเจกต์แรกของคุณ
เมื่อคุณเข้าใจการจัดการไฟล์ด้วย fs module แล้ว สิ่งสำคัญที่สุดคือการเอาไปลองใช้จริงในโปรเจกต์ส่วนตัว ลองสร้างโปรเจกต์ง่ายๆ เช่น Simple CLI Note-taking App (โปรแกรมจดบันทึกผ่านหน้าจอคำสั่ง) ที่สามารถรับข้อความจากคีย์บอร์ดแล้วบันทึกลงในไฟล์ .txt ได้ โดยใช้ความรู้เรื่อง writeFile และ appendFile ที่เราเรียนกันไป
การลองทำโปรเจกต์เล็กๆ จะช่วยให้คุณเห็นปัญหาจริง เช่น การจัดการไฟล์ที่ไม่มีอยู่จริง หรือการจัดการสิทธิ์การเข้าถึง ซึ่งสิ่งเหล่านี้คือบทเรียนที่ไม่มีในตำราเล่มไหน การเป็นโปรแกรมเมอร์ที่เก่งไม่ได้วัดกันที่จำคำสั่งได้กี่ตัว แต่วัดกันที่การนำคำสั่งที่มีอยู่มาแก้ปัญหาให้ผู้ใช้งานได้จริงและปลอดภัย
ขอให้จำไว้ว่าทุกบรรทัดที่คุณเขียนคือประสบการณ์ อย่ากลัวที่จะทำผิดพลาดในระหว่างการฝึก เพราะการแก้บั๊ก (การแก้ไขจุดที่โปรแกรมทำงานผิดพลาด) คือวิธีเรียนรู้ที่เร็วที่สุดสำหรับโปรแกรมเมอร์ทุกคน ขอให้สนุกกับการเขียนโค้ดและสร้างสรรค์ผลงานของคุณออกมาให้เต็มที่ครับ