עבודה עם קבצים (fs ו-path)
אחד הדברים הראשונים ש-Node.js נותן ושדפדפן לא: גישה למערכת הקבצים. קריאת קובץ הגדרות, כתיבת לוג, יצירת תיקיות - כל אלה עוברים דרך שני מודולים מובנים: fs (File System) ו-path.
שלוש דרכים לאותה פעולה
למודול fs יש שלושה סגנונות, ובקוד ישן תפגשו את כולם:
| סגנון | דוגמה | מתי |
|---|---|---|
| Promises (מודרני) | await readFile(path, 'utf8') | ברירת המחדל לכל קוד חדש |
| סינכרוני | readFileSync(path, 'utf8') | רק בסקריפטים קצרים או בעליית התוכנית |
| Callbacks (ישן) | readFile(path, 'utf8', (err, data) => {}) | בקוד ישן בלבד |
הגרסה הסינכרונית חוסמת את ה-Event Loop: בזמן שהקובץ נקרא, השרת לא עונה לאף משתמש. בשרת משתמשים תמיד בגרסת ה-Promises.
קריאת קובץ
import { readFile } from 'node:fs/promises';
try {
const text = await readFile('notes.txt', 'utf8');
console.log(text);
} catch (error) {
if (error.code === 'ENOENT') {
console.log('הקובץ לא קיים');
} else {
throw error;
}
}
- הקידומת
node:מסמנת שזה מודול מובנה ולא חבילה מ-npm. היא לא חובה, אבל מומלצת. - בלי
'utf8'מקבליםBuffer(בייטים גולמיים) במקום מחרוזת. - ב-ES Module אפשר לכתוב
awaitישירות ברמה העליונה של הקובץ (Top-level await).
כתיבה והוספה
import { writeFile, appendFile } from 'node:fs/promises';
// יוצר את הקובץ, או דורס אותו אם הוא קיים
await writeFile('report.txt', 'דוח יומי\n', 'utf8');
// מוסיף לסוף הקובץ בלי למחוק את מה שהיה
await appendFile('app.log', `${new Date().toISOString()} השרת עלה\n`);
קריאה וכתיבה של JSON
זה הצירוף הכי נפוץ בפועל: קובץ הגדרות או "מסד נתונים" קטן.
import { readFile, writeFile } from 'node:fs/promises';
async function loadSettings() {
const text = await readFile('settings.json', 'utf8');
return JSON.parse(text);
}
async function saveSettings(settings) {
// הפרמטר השלישי מוסיף הזחה, כדי שהקובץ יהיה קריא
await writeFile('settings.json', JSON.stringify(settings, null, 2));
}
const settings = await loadSettings();
settings.theme = 'dark';
await saveSettings(settings);
תיקיות
import { mkdir, readdir, rm, stat } from 'node:fs/promises';
// recursive: יוצר גם תיקיות ביניים, ולא נכשל אם התיקייה קיימת
await mkdir('output/2026/reports', { recursive: true });
// רשימת קבצים, עם סוג כל פריט
const entries = await readdir('output', { withFileTypes: true });
for (const entry of entries) {
console.log(entry.name, entry.isDirectory() ? '(תיקייה)' : '(קובץ)');
}
// גודל ותאריך שינוי
const info = await stat('settings.json');
console.log(info.size, 'בייטים, עודכן', info.mtime);
// מחיקת תיקייה עם כל התוכן שלה - בזהירות!
await rm('output/temp', { recursive: true, force: true });
path: לבנות נתיבים נכון
Windows משתמש ב-\ ו-Mac/Linux ב-/. חיבור נתיבים עם + שובר קוד בין מערכות. path מטפל בזה:
import path from 'node:path';
path.join('data', 'users', 'list.json'); // data/users/list.json (או \ ב-Windows)
path.resolve('data'); // נתיב מוחלט מהתיקייה הנוכחית
path.basename('/tmp/photo.png'); // 'photo.png'
path.extname('photo.png'); // '.png'
path.dirname('/tmp/photo.png'); // '/tmp'
נתיב ביחס לקובץ עצמו
נתיב יחסי כמו 'data.json' נמדד מהתיקייה שממנה הרצתם את node, לא מהתיקייה של הקובץ. כדי לקרוא קובץ שיושב ליד הקוד, בונים נתיב מהתיקייה של הקובץ:
import path from 'node:path';
// ES Modules, Node.js 20.11 ומעלה
const dataFile = path.join(import.meta.dirname, 'data.json');
// בקוד CommonJS ישן: path.join(__dirname, 'data.json')
קבצים גדולים: Streams
readFile טוען את כל הקובץ לזיכרון. לקובץ של כמה גיגה-בייט זה לא יעבוד. Stream קורא את הקובץ בחתיכות:
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';
// ספירת שורות בקובץ לוג ענק, בלי לטעון אותו כולו
const lines = createInterface({ input: createReadStream('huge.log') });
let count = 0;
for await (const line of lines) {
if (line.includes('ERROR')) count++;
}
console.log(`נמצאו ${count} שגיאות`);
דוגמאות מלאות להרצה
read-file.js- קריאה בשלושת הסגנונות, כולל JSONwrite-file.js- כתיבה, הוספה וקבצים זמנייםdirectories.js- יצירה, סריקה ומחיקה של תיקיות
סיכום
- בקוד חדש:
node:fs/promisesעםawait, ו-try/catchסביב כל פעולה. - שגיאת
ENOENTפירושה "הקובץ לא קיים". - בונים נתיבים עם
path.join, ונתיב ליד הקוד עםimport.meta.dirname. - לקבצים גדולים: Streams.
בדקו את עצמכם
נסו לענות לבד לפני שאתם פותחים את התשובה.
-
מה אומרת שגיאה עם
code === 'ENOENT'?הצגת התשובה
תשובה א. Error NO ENTry: הנתיב לא נמצא.
-
איך בונים נתיב לקובץ שיושב ליד הקוד, בקובץ ES Module?
הצגת התשובה
תשובה ג. נתיב יחסי נמדד מהתיקייה שממנה הריצו את node, לא מהקובץ.
-
מתי כדאי Stream במקום readFile?
הצגת התשובה
תשובה ג. Stream קורא בחתיכות.