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
- استخدم
fs.readFile/fs.writeFile/fs.appendFile/fs.unlinkلإجراء عمليات على الملفات - استخدم
fs.mkdir/fs.readdir/fs.stat/fs.existsSyncلتنفيذ عمليات الدليل - التمييز بين الاختلافات في التنفيذ بين الطرق المتزامنة وغير المتزامنة
- فهم نموذج «الاستدعاء التلقائي عند حدوث الخطأ أولاً»
(err, data) - استخدم واجهة برمجة التطبيقات
fs.promisesلتنفيذ عمليات على الملفات باستخدام «Promises» - حدد ترميز الملف المناسب (utf8 / base64 / ثنائي)
2. نظرة عامة على وحدة fs
يُعد المكون fs مكونًا مدمجًا في Node.js مخصصًا لعمليات نظام الملفات، ويوفر إمكانيات مثل قراءة الملفات وكتابتها، وإدارة الدلائل، والتحقق من الأذونات. وعادةً ما تتوفر كل عملية بثلاثة أنماط: متزامنة، و«callback» غير متزامن، و«Promise» غير متزامن.
| ميزة | طريقة متزامنة | طريقة الاستدعاء غير المتزامنة | طريقة fs.promises |
|---|---|---|---|
| ميزة التسمية | xxxSync اللاحقة |
بدون لاحقة | fs.promises.xxx |
| قيمة الإرجاع | تُرجع النتيجة مباشرةً | indefinido، التي تم الحصول عليها عبر استدعاء مرتد |
تُرجع Promise |
| يوقف حلقة الأحداث | نعم | لا | لا |
| معالجة الأخطاء | try/catch |
المعلمة الأولى لدالة الاستدعاء | .catch() / try-catch |
| حالات الاستخدام الموصى بها | تحميل التكوين عند بدء التشغيل | التوافق مع الإصدارات السابقة | الخيار الأمثل للمشاريع الجديدة |
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) بمجرد اكتمال عملية الملف.
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
▶ مثال: القراءة المتزامنة تؤدي إلى توقف البرنامج بأكمله
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.');
Start Reading...
Reading complete,Number of lines:50000
This line must wait until the data has finished loading before it is executed.
▶ مثال: القراءة غير المتزامنة دون حجب حلقة الأحداث
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');
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) |
المسار | حذف الملفات في وقت واحد |
▶ مثال: الكتابة إلى الملفات وإضافة البيانات إليها
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);
});
});
});
Write complete
Addition Complete
Document Content:
Content on the first line
The second line added
▶ مثال: استخدام fs.promises لتجنب الدوال المرتدة المتداخلة
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();
▶ مثال: حذف ملف
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) |
المسار | التحقق من وجود المسار |
▶ مثال: إنشاء مجلد وعرض محتوياته
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();
The directory was successfully created
Table of Contents: [ 'app.log', 'error.log' ]
▶ مثال: تحديد ما إذا كان الملف أم المجلد
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();
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') |
▶ مثال: مقارنة بين طريقتين لمعالجة الأخطاء
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();
Callback Method - Error: ENOENT
PromiseMethod - Error: ENOENT
7. ترميز الملفات
| الرمز | الوصف | السيناريوهات التي ينطبق عليها | مثال |
|---|---|---|---|
'utf8' |
ترميز النص UTF-8 (افتراضي) | قراءة وكتابة الملفات النصية | السجلات، ملفات التكوين، JSON |
'base64' |
ترميز Base64 | نقل الصور، وتحويل الملفات الثنائية إلى نص | الصور المضمنة، ومرفقات البريد الإلكتروني |
'binary' ('latin1') |
البايتات الأولية | العمليات الثنائية للملفات على المستوى المنخفض | معالجة الصور والأرشيفات |
null |
العودة إلى كائن المخزن المؤقت | الحاجة إلى معالجة البايتات الأولية | التحقق من الملفات، معالجة التدفق |
▶ مثال: قراءة الملف نفسه باستخدام ترميزات مختلفة
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();
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. مثال شامل: أداة إدارة الملفات
إنشاء سير عمل كامل لإدارة الملفات: إنشاء دليل → كتابة التكوين → القراءة والتحليل → الإضافة إلى السجل → عرض المحتويات.
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();
✓ 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؟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، ولن يظهر أي خطأ حتى لو كان المجلد موجودًا بالفعل.📖 ملخص
- المفاهيم الأساسية وكيفية تطبيقها
- نظرة عامة على المفاهيم الأساسية واستخدامات وحدة fs
- المفاهيم الأساسية واستخدامات تسلسلات التنفيذ المتزامنة مقابل غير المتزامنة
- المفاهيم الأساسية واستخدامات عمليات قراءة الملفات وكتابتها
- المفاهيم الأساسية لعمليات الدليل وكيفية استخدامها
- المفاهيم الأساسية واستخدامات آليات الاستدعاء التلقائي التي تعتمد على الأخطاء أولاً مقارنةً بالوعود
- المفاهيم الأساسية لترميز الملفات وكيفية استخدامها
- مثال شامل: المفاهيم الأساسية واستخدامات أدوات إدارة الملفات
📝 تمارين
- أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
- قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
- راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
- التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
- حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.