Node.js: المخازن المؤقتة

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

أثناء تطوير خدمة تحميل الصور، لاحظ بوب أن ملفات JPEG التي يقوم المستخدمون بتحميلها تتحول إلى سلسلة من الأحرف المشوشة بعد قراءتها بواسطة fs.readFile — حيث يتم تخزين سلاسل JavaScript باستخدام ترميز UTF-16، الذي لا يستطيع تمثيل جميع قيم البايتات بين 0x00 و 0xFF بشكل صحيح. واكتشف أن Node.js يوفر كائنًا عالميًا Buffer مصممًا خصيصًا للتعامل مع البيانات الثنائية. وهي تعمل ككتلة من الذاكرة الخام، حيث يتوافق كل موضع تمامًا مع بايت واحد. بدءًا من قراءة الملفات وكتابتها وصولاً إلى الإرسال عبر الشبكة، ومن معالجة الصور إلى الحسابات التشفيرية، يُعد Buffer الأداة الأساسية لمعالجة البيانات الثنائية في Node.js.

1. ما ستتعلمه



2. ما هو المخزن المؤقت؟

Buffer هو كائن عالمي توفره Node.js ويُستخدم لتخصيص منطقة ذات حجم ثابت من البيانات الثنائية الأولية خارج كومة V8. يشغل كل عنصر 1 بايت (8 بت)، وتتراوح قيمه بين 0 و255. لا يتطلب Buffer وجود require، وهو جاهز للاستخدام فورًا.

JAVASCRIPT
const buf = Buffer.alloc(4);
console.log(buf);
console.log(buf.length);
TEXT 📖 للعرض فقط
<Buffer 00 00 00 00>
4
100%
flowchart LR
  subgraph Input["Input Source"]
    D[Disk Files]
    N[Network Request]
    C[Cryptographic Operations]
  end

  subgraph Core["Node.js Runtime"]
    B[Buffer<br/>Raw binary data]
    S[String / JSON<br/>Structured Data]
  end

  D -->|Binary Reading| B
  N -->|Raw bytes| B
  C -->|Hash/Signature| B
  B -->|toString / decode| S
  S -->|Buffer.from / encode| B
  B -->|Binary Writing| D
  B -->|Raw bytes| N


3. كيفية إنشاء منطقة عازلة

يوفر Node.js عدة طرق لإنشاء «Buffer»، وتناسب كل طريقة سيناريوهات مختلفة.

الطريقة محتوى التهيئة الأمان الأداء حالات الاستخدام
Buffer.alloc(size) ملء 0 آمن بطيء يتطلب مخزنًا مؤقتًا جديدًا خاليًا
Buffer.allocUnsafe(size) لم يتم تهيئتها (بيانات قديمة) غير آمنة سريعة حساسة للأداء ويتم ملؤها فورًا
Buffer.from(array) قيمة عنصر المصفوفة الأمان عام تم إنشاؤها من مصفوفة بايت
Buffer.from(string, encoding) سلسلة مشفرة الأمان عام إنشاء من سلسلة
Buffer.from(buffer) نسخ محتوى الحافظة الأمان عام استنساخ الحافظة

▶ مثال: alloc مقابل allocUnsafe

JAVASCRIPT
const safe = Buffer.alloc(8);
console.log('alloc:', safe);

const unsafe = Buffer.allocUnsafe(8);
console.log('allocUnsafe:', unsafe);
▶ جرّب الكود
TEXT 📖 للعرض فقط
alloc: <Buffer 00 00 00 00 00 00 00 00>
allocUnsafe: <Buffer a0 3f 1b 00 00 00 00 00>

▶ مثال: الإنشاء من المصفوفات والسلاسل

JAVASCRIPT
const fromArr = Buffer.from([72, 101, 108, 108, 111]);
console.log('from array:', fromArr.toString());

const fromStr = Buffer.from('Hello', 'utf8');
console.log('from string:', fromStr.toString());

const fromHex = Buffer.from('48656c6c6f', 'hex');
console.log('from hex:', fromHex.toString());
▶ جرّب الكود
TEXT 📖 للعرض فقط
from array: Hello
from string: Hello
from hex: Hello


4. الترميز وفك الترميز

يدعم Node.js عدة أنظمة ترميز للأحرف، ويمكن لـ Buffer التحويل بينها بحرية.

الرمز الوصف عدد البايتات لكل حرف الاستخدامات الشائعة
utf8 ترميز يونيكود متغير الطول 1–4 معالجة النصوص (الترميز الافتراضي)
ASCII ASCII 7 بت 1 نص باللغة الإنجليزية العادية
base64 مشفَّر بتنسيق Base64 حوالي 4/3 من الحجم الأصلي مضمن في صورة أو مرفق بريد إلكتروني
hex التمثيل السداسي العشري 2 إخراج التصحيح، عرض التجزئة
ثنائي / latin1 التعيين المباشر للأحرف لكل بايت 1 العمليات بايتًا بايتًا

▶ مثال: تحويل الترميز

JAVASCRIPT
const text = 'Node.js Buffer';

const utf8Buf = Buffer.from(text, 'utf8');
console.log('utf8 bytes:', utf8Buf.length);

const base64 = utf8Buf.toString('base64');
console.log('base64:', base64);

const hex = utf8Buf.toString('hex');
console.log('hex:', hex);

const decoded = Buffer.from(base64, 'base64').toString('utf8');
console.log('decoded:', decoded);
▶ جرّب الكود
TEXT 📖 للعرض فقط
utf8 bytes: 14
base64: Tm9kZS5qcyDliIbku6znqIvl
hex: 4e6f64652e6a7320e7bc93e586b2e58cba
decoded: Node.js Buffer

▶ مثال: بيانات صورة مشفرة بتنسيق Base64

JAVASCRIPT
const fs = require('fs');

const imgBuf = fs.readFileSync('logo.png');
const dataUri = 'data:image/png;base64,' + imgBuf.toString('base64');
console.log('Data URI length:', dataUri.length);
▶ جرّب الكود

5. التحويل بين «Buffer» و«String»

يُعد التحويل بين Buffer وString إحدى العمليات الأكثر شيوعًا في أعمال التطوير اليومية.

الاتجاه الطريقة الوصف
سلسلة → مخزن مؤقت Buffer.from(str, encoding) الترميز الافتراضي هو utf8
Buffer → String buf.toString(encoding) الترميز الافتراضي هو utf8
طول سلسلة الاستعلام بالبايت Buffer.byteLength(str, encoding) تُرجع عدد البايتات بدلاً من عدد الأحرف

▶ مثال: عدد الأحرف مقابل عدد البايتات

JAVASCRIPT
const str = 'Hello, World';
console.log('Number of characters:', str.length);
console.log('Number of bytes (utf8):', Buffer.byteLength(str, 'utf8'));
console.log('Number of bytes (ascii):', Buffer.byteLength(str, 'ascii'));

const buf = Buffer.from(str, 'utf8');
console.log('buf.length:', buf.length);
console.log('Restore the سلسلة أحرف:', buf.toString('utf8'));
▶ جرّب الكود
TEXT 📖 للعرض فقط
Number of characters: 4
Number of bytes (utf8): 12
Number of bytes (ascii): 4
buf.length: 12
Restore the string: Hello, World


6. كيفية استخدام Buffer

(1) جدول مرجعي سريع لطرق المخازن المؤقتة الشائعة

الطريقة الغرض القيمة المرجعة
Buffer.alloc(size, fill) إنشاء وتعبئة مخزن مؤقت جديد
Buffer.from(source, enc) إنشاء من المصدر مخزن مؤقت جديد
Buffer.concat(list, totalLen) ربط عدة مخازن مؤقتة مخزن مؤقت جديد
Buffer.isBuffer(obj) التحقق مما إذا كان عبارة عن مخزن مؤقت قيمة منطقية
Buffer.byteLength(str, enc) طول السلسلة بالبايت رقم
buf.slice(start, end) عرض اللقطة (الذاكرة المشتركة) عرض المخزن المؤقت
buf.subarray(start, end) الشريحة نفسها عرض المخزن المؤقت
buf.toString(enc) تحويل إلى سلسلة نصية سلسلة نصية
buf.write(str, offset, enc) كتابة سلسلة أحرف إلى المخزن المؤقت عدد البايتات المراد كتابتها
buf.copy(target, tStart, sStart, sEnd) نسخ إلى مخزن الوجهة عدد البايتات المراد نسخها
buf.equals(otherBuf) مقارنة ما إذا كان المحتوى متطابقًا قيمة منطقية
buf.compare(otherBuf) مقارنة معجمية -1 / 0 / 1
buf.fill(value, start, end) ملء النطاق المحدد المخزن المؤقت الأصلي
buf.indexOf(value, byteOffset) البحث عن موضع البايت رقم

▶ مثال: التسلسل باستخدام concat

JAVASCRIPT
const part1 = Buffer.from('Hello, ');
const part2 = Buffer.from('Buffer!');
const merged = Buffer.concat([part1, part2]);
console.log(merged.toString());
console.log('total length:', merged.length);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Hello, Buffer!
total length: 14

▶ مثال: طرق عرض الشرائح ومشاركة الذاكرة

JAVASCRIPT
const original = Buffer.from('ABCDEFGH');
const sliced = original.slice(0, 4);

sliced[0] = 88;
console.log('original:', original.toString());
console.log('sliced:', sliced.toString());
▶ جرّب الكود
TEXT 📖 للعرض فقط
original: XBCDEFGH
sliced: XBCD

▶ مثال: الكتابة والنسخ

JAVASCRIPT
const buf = Buffer.alloc(16);
buf.write('Hi', 0, 'utf8');
buf.write('There', 2, 'utf8');
console.log('after write:', buf.toString('utf8', 0, 7));

const src = Buffer.from('COPY');
const dest = Buffer.alloc(8);
src.copy(dest, 2);
console.log('after copy:', dest.toString());
▶ جرّب الكود
TEXT 📖 للعرض فقط
after write: HiThere
after copy:   COPY


7. Buffer و TypedArray

تشترك الذاكرة الأساسية لـ Buffer في نفس آلية ArrayBuffer التي تتبعها TypedArray في ES2015. وتُعد Buffer في الأساس فئة فرعية من Uint8Array، لكنها تحتوي على طرق إضافية خاصة بـ Node.js.

خاصية Buffer Uint8Array ArrayBuffer
المصدر الكائن العام في Node.js مدمج في ES2015 مدمج في ES2015
الآلية الأساسية قائمة تستند إلى ArrayBuffer قائمة تستند إلى ArrayBuffer ذاكرة ثنائية خام
ترتيب البايتات يعتمد على النظام الأساسي يعتمد على النظام الأساسي لا يوجد مفهوم لترتيب البايتات
الطرق الخاصة toString/slice/concat، إلخ طرق TypedArray القياسية byteLength فقط
طريقة الإنشاء Buffer.alloc/Buffer.from new Uint8Array() new ArrayBuffer()
التوافق بين الوحدات يمكن تمريرها إلى المكونات الإضافية لـ C++ يمكن تمريرها إلى واجهات برمجة التطبيقات على الويب تنسيق أساسي موحد

▶ مثال: التحويل بين Buffer و Uint8Array

JAVASCRIPT
const buf = Buffer.from([1, 2, 3, 4, 5]);

const uint8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
console.log('Uint8Array:', uint8);

const backToBuf = Buffer.from(uint8.buffer);
console.log('Buffer:', backToBuf);
console.log('isBuffer:', Buffer.isBuffer(backToBuf));
▶ جرّب الكود
TEXT 📖 للعرض فقط
Uint8Array: Uint8Array(5) [1, 2, 3, 4, 5]
Buffer: <Buffer 01 02 03 04 05>
isBuffer: true

▶ مثال: قراءة القيم متعددة البايت باستخدام DataView

JAVASCRIPT
const buf = Buffer.alloc(4);
buf.writeUInt32BE(0x12345678, 0);
console.log('big-endian:', buf.toString('hex'));

const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
console.log('read as uint32:', view.getUint32(0, false));
console.log('read as uint16:', view.getUint16(0, false));
▶ جرّب الكود
TEXT 📖 للعرض فقط
big-endian: 12345678
read as uint32: 305419896
read as uint16: 4660


8. قراءة وكتابة الملفات الثنائية

عند استخدام fs.readFile لقراءة ملف دون تحديد الترميز، تكون القيمة المرجعة عبارة عن مخزن مؤقت وليس سلسلة نصية. وهذه هي الطريقة القياسية للتعامل مع الملفات الثنائية مثل الصور والملفات الصوتية وملفات الفيديو.

▶ مثال: قراءة ملف ثنائي

JAVASCRIPT
const fs = require('fs');

const imgBuf = fs.readFileSync('photo.jpg');
console.log('isBuffer:', Buffer.isBuffer(imgBuf));
console.log('size:', imgBuf.length, 'bytes');
console.log('first 8 bytes (hex):', imgBuf.slice(0, 8).toString('hex'));

const isJPEG = imgBuf[0] === 0xFF && imgBuf[1] === 0xD8;
const isPNG = imgBuf[0] === 0x89 && imgBuf[1] === 0x50;
console.log('isJPEG:', isJPEG);
console.log('isPNG:', isPNG);
▶ جرّب الكود
TEXT 📖 للعرض فقط
isBuffer: true
size: 245760 bytes
first 8 bytes (hex): ffd8ffe000104a46
isJPEG: true
isPNG: false

▶ مثال: تطبيقات المخازن المؤقتة في الشبكات وعلم التشفير

JAVASCRIPT
const crypto = require('crypto');

const data = Buffer.from('important message', 'utf8');

const hash = crypto.createHash('sha256').update(data).digest();
console.log('sha256 (hex):', hash.toString('hex'));

const hmac = crypto.createHmac('sha256', 'secret-key').update(data).digest();
console.log('hmac (hex):', hmac.toString('hex'));

const randomBytes = crypto.randomBytes(16);
console.log('random (hex):', randomBytes.toString('hex'));
▶ جرّب الكود
TEXT 📖 للعرض فقط
sha256 (hex): 8c8821c72b56a55724e9ad64b875e4b62e6c5e9f9c4c4b0c4d5e6f7a8b9c0d1e
hmac (hex): a3f2b8c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0
random (hex): 3a7f2b1c9d4e8a6f5b0c7d2e1f3a4b8c


9. مثال شامل: أداة ترميز الملفات

قم بإنشاء أداة بسيطة لترميز الملفات: قراءة ملف ثنائي → تحويله إلى Base64 → كتابته في ملف نصي → فك الترميز لاستعادة البيانات الأصلية.

(1) مقارنة بين «Buffer» و«String» و«TypedArray»

البعد المخزن المؤقت السلسلة TypedArray
المحتوى المخزن البايتات الأولية نقطة الترميز UTF-16 القيمة الخاصة بالنوع
حجم العنصر ثابت عند 1 بايت 2 بايت (UTF-16) 1–8 بايت
أنواع البيانات المدعومة البيانات الثنائية، الملفات، الشبكة النص المصفوفات الرقمية، WebGL
قابل للتغيير قابل للتعديل غير قابل للتغيير قابل للتعديل
معالجة البايتات الصفرية التخزين العادي اقتطاع السلاسل التخزين العادي
دعم الترميز UTF-8/Base64/السداسي عشري، إلخ. UTF-16 فقط لا يوجد مفهوم للترميز
JAVASCRIPT
const fs = require('fs');
const path = require('path');

الدالة encodeFile(inputPath, outputPath) {
  const raw = fs.readFileSync(inputPath);
  const base64 = raw.toString('base64');
  fs.writeFileSync(outputPath, base64, 'utf8');
  console.log(`Encoded: ${raw.length} bytes → ${base64.length} chars`);
  return { originalSize: raw.length, encodedSize: base64.length };
}

الدالة decodeFile(inputPath, outputPath) {
  const base64Str = fs.readFileSync(inputPath, 'utf8');
  const decoded = Buffer.from(base64Str, 'base64');
  fs.writeFileSync(outputPath, decoded);
  console.log(`Decoded: ${base64Str.length} chars → ${decoded.length} bytes`);
  return { encodedSize: base64Str.length, decodedSize: decoded.length };
}

الدالة verify(originalPath, restoredPath) {
  const a = fs.readFileSync(originalPath);
  const b = fs.readFileSync(restoredPath);
  if (a.equals(b)) {
    console.log('Verification: PASSED - files are identical');
  } else {
    console.log('Verification: FAILED - files differ');
  }
}

const inputPath = path.الربط(__dirname, 'sample.dat');
const encodedPath = path.الربط(__dirname, 'sample.b64.txt');
const restoredPath = path.الربط(__dirname, 'sample.restored.dat');

const sampleData = Buffer.alloc(256);
for (let i = 0; i < 256; i++) {
  sampleData[i] = i;
}
fs.writeFileSync(inputPath, sampleData);

encodeFile(inputPath, encodedPath);
decodeFile(encodedPath, restoredPath);
verify(inputPath, restoredPath);
TEXT 📖 للعرض فقط
Encoded: 256 bytes → 344 chars
Decoded: 344 chars → 256 bytes
Verification: PASSED - files are identical


❓ أسئلة شائعة

س ما الفرق بين Buffer.alloc وBuffer.allocUnsafe؟
ج يقوم Buffer.alloc(size) بتهيئة كل بايت إلى 0؛ وهو آمن ولكنه أبطأ قليلاً. أما Buffer.allocUnsafe(size) فلا يقوم بتهيئة البايتات؛ وقد تحتوي على بيانات قديمة من الذاكرة. ويوفر أداءً أسرع، ولكن يجب ملؤها على الفور، وإلا فقد تتسرب معلومات حساسة.
س هل تُرجع الدالة buf.length عدد البايتات أم عدد الأحرف؟
ج تُرجع الدالة buf.length عدد البايتات، على عكس الدالة str.length الخاصة بالسلسلة النصية (التي تُرجع عدد وحدات الترميز UTF-16). على سبيل المثال، str.length لـ 'مرحبا' يساوي 2، لكن Buffer.byteLength('مرحبا') يساوي 6 (كل حرف صيني يساوي 3 بايت في UTF-8).
س لماذا لا نستخدم String لمعالجة البيانات الثنائية؟
ج تستخدم سلاسل JavaScript ترميز UTF-16، الذي لا يمكنه تمثيل البايت 0x00 بشكل صحيح (حيث يتم اقتطاعه)، ولا يوجد توزيع مباشر بين الأحرف متعددة البايتات والبايتات. كل موضع في Buffer يتوافق تمامًا مع بايت واحد، مما يجعلها الحاوية الصحيحة للبيانات الثنائية.
س هل يُعد «Buffer» جزءًا من JavaScript؟
ج لا. «Buffer» هو كائن عالمي خاص بـ Node.js ولا يُعد جزءًا من مواصفات ECMAScript. لا يوجد «Buffer» في بيئة المتصفح؛ بل يتم تمثيله بدلاً من ذلك بواسطة واجهات برمجة تطبيقات الويب (Web APIs) مثل Uint8Array وArrayBuffer.
س كيف يمكنني تحديد ما إذا كانت القيمة «value» عبارة عن «Buffer»؟
ج استخدم Buffer.isBuffer(obj)؛ فستُرجع true أو false. لا تستخدم instanceof، لأنها قد تفشل عند عبور العوالم (مثل وحدات VM المختلفة).
س ما الفرق بين buf.slice() وbuf.subarray()؟
ج في Node.js، يتصرفان بشكل متطابق؛ فكلاهما يُرجعان طرق عرض تشترك في الذاكرة الأساسية. تمت إضافة subarray للحفاظ على اتساق التسمية مع Uint8Array.プロトタイプ.subarray، بينما تم الاحتفاظ بـ slice من أجل التوافق مع الإصدارات السابقة. يوصى باستخدام subarray في الكود الجديد.
س كيف يمكنني تحويل Buffer إلى JSON بأمان؟
ج سيؤدي استخدام buf.toJSON() إلى إرجاع كائن بتنسيق { type: 'Buffer', data: [...] }. يمكنك أيضًا استخدام JSON.stringify(buf) مباشرةً، حيث سيؤدي ذلك إلى استدعاء toJSON() تلقائيًّا. بعد إزالة التسلسل، استخدم Buffer.from(obj.data) لاستعادة البيانات الأصلية.

📖 ملخص


📝 تمارين

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

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

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

100%