fs.readFile(path, 'utf8', callback) קורא קובץ שלם ומעביר את התוכן שלו ל-callback כמחרוזת. fs.readFileSync(path, 'utf8') מחזיר את המחרוזת ישירות ומחכה שהקריאה תסתיים, ו-await readFile(path, 'utf8') מתוך fs/promises עושה את אותו הדבר בלי לחסום. אם משמיטים את 'utf8', מקבלים Buffer של בייטים גולמיים במקום טקסט.
readFile called מודפס ראשון: readFile רק מתחיל את הקריאה, וה-callback רץ כשהתוכן מוכן. מחקו את 'utf8', והריצו שוב: ה-callback מקבל <Buffer 42 75 79 20 6d 69 6c 6b 0a 43 61 6c 6c 20 41 64 61>, הבייטים הגולמיים של הקובץ.
readFile מול readFileSync מול fs/promises
שלושתם קוראים את אותו קובץ. ההבדל הוא איך מקבלים את התוצאה והאם שאר התוכנית מחכה.
השורה הסינכרונית מודפסת ראשונה, כי שום דבר אחר לא רץ עד שהיא חוזרת. שתי האחרות מסתיימות אחרי הקוד הראשי, לפי הסדר שבו הקריאות שלהן מסתיימות.
readFileSync | readFile (callback) | readFile של fs/promises | |
|---|---|---|---|
| מחזיר | את התוכן | undefined | promise של התוכן |
| חוסם את התוכנית | כן | לא | לא |
| שגיאות | נזרקות, השתמשו ב-try/catch | הארגומנט הראשון של ה-callback | promise שנדחה, try/catch עם await |
| מתי להשתמש | קוד עלייה וסקריפטים | קוד callbacks ישן | קוד חדש |
השתמשו ב-readFileSync כשקוראים קובץ הגדרות בעלייה או בסקריפט קצר. בשרת, קריאה סינכרונית עוצרת כל בקשה אחרת בזמן שהדיסק עובד, אז שם השתמשו בגרסת ה-promise. הדף על fs/promises מכסה את שאר ה-API הזה.
טיפול בקובץ חסר (ENOENT)
כשהקובץ לא קיים, לשגיאה יש code: 'ENOENT' ("error, no entry"). בדקו את err.code כדי להבדיל בין קובץ חסר לבין כשלים אחרים, כמו בעיית הרשאות.
הקריאה הראשונה לא מוצאת קובץ וחוזרת ל-default settings; השנייה קוראת dark mode. זריקה מחדש של שאר השגיאות משאירה בעיות אמיתיות גלויות, במקום להשתמש בשקט בברירות מחדל.
fs.existsSync: למה פשוט לנסות לקרוא
fs.existsSync(path) מחזיר true או false, ומפתה לבדוק לפני הקריאה:
if (fs.existsSync('data.txt')) {
const text = fs.readFileSync('data.txt', 'utf8');
}
אלה שני צעדים כשאחד מספיק, והקובץ יכול להימחק ביניהם, כך שהקריאה עדיין יכולה לזרוק. נסו לקרוא וטפלו ב-ENOENT, כמו למעלה. existsSync בסדר כשצריך רק לדעת אם משהו קיים ולא תפתחו אותו, למשל כדי להחליט אם ליצור תיקייה.
קריאת קובץ JSON
קראו את הטקסט, ואז פענחו אותו עם JSON.parse. קובץ עם שגיאת תחביר זורק SyntaxError, אז השאירו אותו בתוך אותו try.
הוא מדפיס 3001 ו-boolean: אחרי הפענוח, לערכים יש את הטיפוסים שלהם מ-JSON. גם require('./config.json') עובד, אבל הוא שומר את התוצאה ב-cache, כך ש-require שני מחזיר את האובייקט הישן גם אחרי שהקובץ השתנה.
קריאת קובץ שורה אחרי שורה
לקובץ שנכנס בזיכרון, קראו אותו כולו ופצלו לפי תו השורה החדשה. סננו את המחרוזת הריקה שתו שורה חדשה בסוף משאיר.
הפיצול נותן 4 פריטים, האחרון ריק, ולכן הלולאה מדפיסה שלוש שורות ממוספרות. קבצים שנכתבו ב-Windows מסיימים שורות ב-\r\n; פצלו לפי /\r?\n/ כדי לטפל בשני המקרים.
לוג של כמה גיגה-בייט לא נכנס בזיכרון. קראו אותו כ-stream, כך שרק שורה אחת מוחזקת בכל פעם:
const fs = require('fs');
const readline = require('readline');
async function main() {
const rl = readline.createInterface({
input: fs.createReadStream('big.log'),
crlfDelay: Infinity,
});
for await (const line of rl) {
console.log(line);
}
}
main();
טעויות נפוצות
שימוש בתוצאה מחוץ ל-callback. const text = fs.readFile(...) הוא undefined. התוכן קיים רק בתוך ה-callback, או אחרי await.
לשכוח את הקידוד. בלי 'utf8' מקבלים Buffer, ו-data.split('\n') זורק data.split is not a function. העבירו את הקידוד בכל פעם שאתם רוצים טקסט.
נתיבים יחסיים לתיקייה הלא נכונה. את 'data.txt' מחפשים במקום שבו node הופעל. השתמשו ב-path.join(__dirname, 'data.txt') לקובץ שמגיע לצד הסקריפט שלכם.
שאלות נפוצות
למה fs.readFile מחזיר undefined?
fs.readFile לא מחזיר את התוכן. הוא מתחיל את הקריאה וקורא ל-callback שלכם מאוחר יותר עם (err, data). השתמשו בנתונים בתוך ה-callback, קראו ל-readFileSync, או עשו await לגרסת ה-promise.
איפה Node מחפש נתיב יחסי כמו 'data.txt'?
בתיקיית העבודה הנוכחית, התיקייה שממנה הרצתם את node, ולא בתיקייה של הסקריפט. כדי לקרוא קובץ שנמצא ליד הסקריפט, בנו את הנתיב עם path.join(__dirname, 'data.txt').
האם fs.readFile יכול לקרוא קובץ מ-URL?
לא, הוא קורא רק ממערכת הקבצים. כדי לקבל קובץ דרך HTTP, השתמשו ב-fetch ואז ב-await res.text().
באיזה גודל קובץ readFile יכול לטפל?
הוא טוען את כל הקובץ לזיכרון, אז הוא מתאים לקבצי הגדרות, תבניות וקבצי נתונים קטנים. קבצים של מאות מגה-בייט קראו בחלקים עם fs.createReadStream.