Node.js: أساسيات نظام الملفات

آخر تحديث: 2026-08-26

Charlie is a バックエンド engineer responsible for maintaining the company’s log analysis platform. Every day at dawn, the system needs to process approximately 50,000 lines of server log files. Initially, he used fs.readFileSync to read the logs one by one, but the entire program froze completely during the reading process, causing all other requests to time out. After switching to asynchronous reading with fs.readFile, the program was able to respond to other requests while waiting for disk I/O, resulting in a 10-fold increase in overall throughput. This experience gave him a deep understanding of the difference between synchronous and asynchronous operations in the Node.js file system API.

1. What You'll Learn



2. نظرة عامة على وحدة fs

يُعد المكون fs مكونًا مدمجًا في Node.js مخصصًا لعمليات نظام الملفات، ويوفر إمكانيات مثل قراءة الملفات وكتابتها، وإدارة الدلائل، والتحقق من الأذونات. وعادةً ما تتوفر كل عملية بثلاثة أنماط: متزامنة، و«callback» غير متزامن، و«Promise» غير متزامن.

ميزة طريقة متزامنة طريقة الاستدعاء غير المتزامنة طريقة fs.promises
ميزة التسمية xxxSync اللاحقة بدون لاحقة fs.promises.xxx
قيمة الإرجاع تُرجع النتيجة مباشرةً indefinido، التي تم الحصول عليها عبر استدعاء مرتد تُرجع Promise
يوقف حلقة الأحداث نعم لا لا
معالجة الأخطاء try/catch المعلمة الأولى لدالة الاستدعاء .catch() / try-catch
حالات الاستخدام الموصى بها تحميل التكوين عند بدء التشغيل التوافق مع الإصدارات السابقة الخيار الأمثل للمشاريع الجديدة
JAVASCRIPT
const fs = require('fs');

// Synchronize
const data = fs.readFileSync('config.json', 'utf8');

// Asynchronous Callbacks
fs.readFile('config.json', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

// Promise
const fsPromises = require('fs/promises');
fsPromises.readFile('config.json', 'utf8')
  .then(data => console.log(data))
  .catch(err => console.error(err));


3. تسلسلات التنفيذ المتزامنة مقابل غير المتزامنة

تعمل الطرق المتزامنة على حجب حلقة الأحداث ولا تواصل تنفيذ التعليمات البرمجية اللاحقة حتى تكتمل عملية الملف. أما الطرق غير المتزامنة، فتُرجع النتيجة فورًا وتُعلم بها عبر استدعاء مرتد أو وعد (Promise) بمجرد اكتمال عملية الملف.

100%
sequenceDiagram
    participant Main as Main Thread
    participant FS_Sync as Synchronous Read
    participant FS_Async as Asynchronous Reading
    participant Disk as Disk I/O

    Note over Main,Disk: Synchronous Execution Process
    Main->>FS_Sync: readFileSync('log.txt')
    FS_Sync->>Disk: Read a File(Blocking Wait)
    Disk-->>FS_Sync: Return Data
    FS_Sync-->>Main: Continue executing the following code
    Note right of Main: All other requests are on hold

    Note over Main,Disk: Asynchronous Execution Flow
    Main->>FS_Async: readFile('log.txt', callback)
    FS_Async->>Disk: Submit a read request
    FS_Async-->>Main: Return Now,Continue Execution
    Note right of Main: Can handle other requests
    Disk-->>FS_Async: I/O Done
    FS_Async-->>Main: Execute the callback function

▶ مثال: القراءة المتزامنة تؤدي إلى توقف البرنامج بأكمله

JAVASCRIPT
const fs = require('fs');

console.log('Start Reading...');
const data = fs.readFileSync('big-log.txt', 'utf8');
console.log('Reading complete,Number of lines:', data.split('\n').length);
console.log('This line must wait until the data has finished loading before it is executed.');
▶ جرّب الكود
TEXT 📖 للعرض فقط
Start Reading...
Reading complete,Number of lines:50000
This line must wait until the data has finished loading before it is executed.

▶ مثال: القراءة غير المتزامنة دون حجب حلقة الأحداث

JAVASCRIPT
const fs = require('fs');

console.log('Start Reading...');
fs.readFile('big-log.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('Reading complete,Number of lines:', data.split('\n').length);
});
console.log('This line is executed immediately,No need to wait for the data to be read');
▶ جرّب الكود
TEXT 📖 للعرض فقط
Start Reading...
This line is executed immediately,No need to wait for the data to be read
Reading complete,Number of lines:50000


4. عمليات قراءة وكتابة الملفات

الطريقة المعلمات الغرض
fs.readFile(path, encoding, callback) المسار، الترميز، دالة الاستدعاء القراءة غير المتزامنة للملف بأكمله
fs.readFileSync(path, encoding) المسار، الترميز قراءة الملف بالكامل بشكل متزامن
fs.writeFile(path, data, encoding, callback) المسار، البيانات، الترميز، دالة الاستدعاء الكتابة غير المتزامنة (الكتابة فوق البيانات الموجودة)
fs.writeFileSync(path, data, encoding) المسار، البيانات، الترميز الكتابة المتزامنة (الكتابة فوق)
fs.appendFile(path, data, encoding, callback) المسار، البيانات، الترميز، استدعاء الرد الإضافة غير المتزامنة
fs.appendFileSync(path, data, encoding) المسار، البيانات، الترميز مزامنة المحتوى الملحق
fs.unlink(path, callback) المسار، دالة الاستدعاء الحذف غير المتزامن للملفات
fs.unlinkSync(path) المسار حذف الملفات في وقت واحد

▶ مثال: الكتابة إلى الملفات وإضافة البيانات إليها

JAVASCRIPT
const fs = require('fs');

fs.writeFile('output.txt', 'Content on the first line\n', 'utf8', (err) => {
  if (err) throw err;
  console.log('Write complete');

  fs.appendFile('output.txt', 'The second line added\n', 'utf8', (err) => {
    if (err) throw err;
    console.log('Addition Complete');

    fs.readFile('output.txt', 'utf8', (err, data) => {
      if (err) throw err;
      console.log('Document Content:\n', data);
    });
  });
});
▶ جرّب الكود
TEXT 📖 للعرض فقط
Write complete
Addition Complete
Document Content:
 Content on the first line
The second line added

▶ مثال: استخدام fs.promises لتجنب الدوال المرتدة المتداخلة

JAVASCRIPT
const fs = require('fs/promises');

async function writeAndRead() {
  try {
    await fs.writeFile('output.txt', 'Content on the first line\n', 'utf8');
    console.log('Write complete');
    await fs.appendFile('output.txt', 'The second line added\n', 'utf8');
    console.log('Addition Complete');
    const data = await fs.readFile('output.txt', 'utf8');
    console.log('Document Content:\n', data);
  } catch (err) {
    console.error('Operation Failed:', err.message);
  }
}

writeAndRead();
▶ جرّب الكود

▶ مثال: حذف ملف

JAVASCRIPT
const fs = require('fs/promises');

async function deleteFile() {
  try {
    await fs.unlink('output.txt');
    console.log('The file has been deleted');
  } catch (err) {
    console.error('Deletion Failed:', err.message);
  }
}

deleteFile();
▶ جرّب الكود

5. عمليات الدليل

الطريقة المعلمات الغرض
fs.mkdir(path, options, callback) المسار، {recursive}، رد الاتصال إنشاء دليل
fs.readdir(path, options, callback) المسار، {withFileTypes}، رد الاتصال عرض محتويات الدليل
fs.stat(path, callback) المسار، وظيفة الاستدعاء الحصول على معلومات الملف/المجلد
fs.existsSync(path) المسار التحقق من وجود المسار

▶ مثال: إنشاء مجلد وعرض محتوياته

JAVASCRIPT
const fs = require('fs/promises');

async function dirOperations() {
  try {
    await fs.mkdir('logs', { recursive: true });
    console.log('The directory was successfully created');

    await fs.writeFile('logs/app.log', '2025-01-01 Server started\n', 'utf8');
    await fs.writeFile('logs/error.log', '2025-01-01 Connection timeout\n', 'utf8');

    const files = await fs.readdir('logs');
    console.log('Table of Contents:', files);
  } catch (err) {
    console.error('Operation Failed:', err.message);
  }
}

dirOperations();
▶ جرّب الكود
TEXT 📖 للعرض فقط
The directory was successfully created
Table of Contents: [ 'app.log', 'error.log' ]

▶ مثال: تحديد ما إذا كان الملف أم المجلد

JAVASCRIPT
const fs = require('fs/promises');

async function checkType() {
  const stats = await fs.stat('logs');
  console.log('logs This is the table of contents:', stats.isDirectory());
  console.log('logs It is a file:', stats.isFile());

  const fileStats = await fs.stat('logs/app.log');
  console.log('app.log It is a file:', fileStats.isFile());
  console.log('File size:', fileStats.size, 'bytes');
}

checkType();
▶ جرّب الكود
TEXT 📖 للعرض فقط
logs This is the table of contents: true
logs It is a file: false
app.log It is a file: true
File size: 29 bytes


6. مقارنة بين دالات الاستدعاء التي تُرجع الخطأ أولاً (Error-First Callbacks) والوعود (Promises)

تتبع واجهة برمجة تطبيقات نظام الملفات في Node.js قاعدة «الاستدعاء المرتد الذي يبدأ بالخطأ»: حيث تكون الحجة الأولى لدالة الاستدعاء المرتد دائمًا كائن خطأ؛ وإذا لم يكن هناك خطأ، فإنها تكون null. ويستخدم fs.promises آلية Promise القياسية لمعالجة الأخطاء.

عنصر المقارنة استدعاء "الخطأ أولاً" fs.promises
توقيع الدالة (err, data) => {} العائد Promise<data>
خطأ في الحكم if (err) تحقق try/catch أو .catch()
مشكلات التداخل عرضة لـ«جحيم الاستدعاءات» async/await التسوية
السيناريوهات النموذجية التوافق مع المشاريع القديمة التوصيات للمشاريع الجديدة
طريقة الاستيراد require('fs') require('fs/promises')

▶ مثال: مقارنة بين طريقتين لمعالجة الأخطاء

JAVASCRIPT
const fsCallback = require('fs');
const fsPromise = require('fs/promises');

// Error-First Callback
fsCallback.readFile('not-exist.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Callback Method - Error:', err.code);
    return;
  }
  console.log(data);
});

// Promise Method
async function readWithPromise() {
  try {
    const data = await fsPromise.readFile('not-exist.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error('PromiseMethod - Error:', err.code);
  }
}

readWithPromise();
▶ جرّب الكود
TEXT 📖 للعرض فقط
Callback Method - Error: ENOENT
PromiseMethod - Error: ENOENT


7. ترميز الملفات

الرمز الوصف السيناريوهات التي ينطبق عليها مثال
'utf8' ترميز النص UTF-8 (افتراضي) قراءة وكتابة الملفات النصية السجلات، ملفات التكوين، JSON
'base64' ترميز Base64 نقل الصور، وتحويل الملفات الثنائية إلى نص الصور المضمنة، ومرفقات البريد الإلكتروني
'binary' ('latin1') البايتات الأولية العمليات الثنائية للملفات على المستوى المنخفض معالجة الصور والأرشيفات
null العودة إلى كائن المخزن المؤقت الحاجة إلى معالجة البايتات الأولية التحقق من الملفات، معالجة التدفق

▶ مثال: قراءة الملف نفسه باستخدام ترميزات مختلفة

JAVASCRIPT
const fs = require('fs/promises');

async function readEncodings() {
  await fs.writeFile('sample.txt', 'Hello The World', 'utf8');

  const utf8Data = await fs.readFile('sample.txt', 'utf8');
  console.log('UTF-8:', utf8Data);

  const base64Data = await fs.readFile('sample.txt', 'base64');
  console.log('Base64:', base64Data);

  const bufferData = await fs.readFile('sample.txt');
  console.log('Buffer:', bufferData);
  console.log('Buffer Hexadecimal:', bufferData.toString('hex'));
}

readEncodings();
▶ جرّب الكود
TEXT 📖 للعرض فقط
UTF-8: Hello The World
Base64: SGVsbG8g5LiW55WM
Buffer: <Buffer 48 65 6c 6c 6f 20 e4 b8 96 e7 95 8c>
Buffer Hexadecimal: 48656c6c6f20e4b896e7958c


8. مثال شامل: أداة إدارة الملفات

إنشاء سير عمل كامل لإدارة الملفات: إنشاء دليل → كتابة التكوين → القراءة والتحليل → الإضافة إلى السجل → عرض المحتويات.

JAVASCRIPT
const fs = require('fs/promises');
const path = require('path');

async function fileManager() {
  const dir = 'project-data';
  const configPath = path.join(dir, 'config.json');
  const logPath = path.join(dir, 'app.log');

  try {
    // Step 1: Create a Directory
    await fs.mkdir(dir, { recursive: true });
    console.log('✓ The table of contents has been created:', dir);

    // Step 2: Write to the configuration file
    const config = {
      appName: 'LogAnalyzer',
      version: '1.0.0',
      maxLines: 50000,
      encoding: 'utf8'
    };
    await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf8');
    console.log('✓ The configuration file has been written.:', configPath);

    // Step 3: Read and Parse the Configuration
    const raw = await fs.readFile(configPath, 'utf8');
    const parsed = JSON.parse(raw);
    console.log('✓ Configuration loaded:', parsed.appName, 'v' + parsed.version);

    // Step 4: Add a log entry
    const timestamp = new Date().toISOString();
    await fs.appendFile(logPath, `[${timestamp}] Service started\n`, 'utf8');
    await fs.appendFile(logPath, `[${timestamp}] Config loaded: ${parsed.maxLines} lines\n`, 'utf8');
    console.log('✓ The log has been appended.:', logPath);

    // Step 5: List the contents of the table of contents
    const entries = await fs.readdir(dir, { withFileTypes: true });
    console.log('✓ Table of Contents:');
    for (const entry of entries) {
      const type = entry.isDirectory() ? '[DIR]' : '[FILE]';
      const stats = await fs.stat(path.join(dir, entry.name));
      console.log(`  ${type} ${entry.name} (${stats.size} bytes)`);
    }
  } catch (err) {
    console.error('✗ Operation Failed:', err.message);
  }
}

fileManager();
TEXT 📖 للعرض فقط
✓ The table of contents has been created: project-data
✓ The configuration file has been written.: project-data/config.json
✓ Configuration loaded: LogAnalyzer v1.0.0
✓ The log has been appended.: project-data/app.log
✓ Table of Contents:
  [FILE] app.log (106 bytes)
  [FILE] config.json (98 bytes)


❓ أسئلة شائعة

س متى ينبغي استخدام الطرق المتزامنة؟
ج استخدمها فقط في العمليات التي تتم مرة واحدة، مثل تحميل ملفات التكوين، أثناء بدء تشغيل التطبيق. لا تستخدم الطرق المتزامنة أبدًا أثناء وقت التشغيل، لأنها ستؤدي إلى حجب حلقة الأحداث.
س لماذا يكون المعامل الأول لدالة الاستدعاء err؟
ج هذه هي قاعدة «دالة الاستدعاء التي تبدأ بالخطأ» في Node.js، والتي تتطلب من المطورين التحقق من وجود أخطاء قبل معالجة البيانات، مما يمنع تجاهل الاستثناءات.
س ما الفرق بين fs.promises وfs؟
ج توفر fs.promises (أو require('fs/promises')) نفس الوظيفة ولكنها تُرجع Promise؛ ويمكن استخدام async/await كبديل لعمليات الاستدعاء المتداخلة؛ fs يستخدم أسلوب الاستدعاء المرتد — وكلاهما متطابقان من الناحية الوظيفية.
س كيف يمكنني تحديد ما إذا كان المسار ملفًا أم مجلدًا؟
ج استخدم fs.stat(path) للحصول على الكائن stats، ثم استدعِ stats.isFile() للتحقق مما إذا كان ملفًا، وstats.isDirectory() للتحقق مما إذا كان مجلدًا.
س هل يقوم readFile بتحميل الملف بأكمله إلى الذاكرة؟
ج نعم، يقوم readFile بقراءة الملف بأكمله إلى الذاكرة. عند التعامل مع الملفات الكبيرة، ينبغي عليك استخدام fs.createReadStream لقراءتها على شكل تدفقات لتجنب تجاوز سعة الذاكرة.
س ما الغرض من الخيار recursive في fs.mkdir؟
ج يتيح لك ضبط { recursive: true } إنشاء مستويات متعددة من المجلدات المتداخلة دفعة واحدة، على غرار mkdir -p، ولن يظهر أي خطأ حتى لو كان المجلد موجودًا بالفعل.

📖 ملخص


📝 تمارين

  1. أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
  2. قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
  3. راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
  4. التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
  5. حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%