← العودة إلى المدونة

كيفية تحليل ملفات JSON الكبيرة دون تعطل في 2026

دليل شامل حول التعامل مع ملفات JSON الكبيرة (100MB+) في JavaScript و Python. تعلم تقنيات البث والتحليل التدريجي والتقسيم لتجنب أخطاء نفاد الذاكرة.

Big JSON Team15 دقيقة للقراءةprogramming
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

15 دقيقة قراءة

# كيفية تحليل ملفات JSON الكبيرة دون تعطل في 2026

المقدمة

هل واجهت من قبل خطأ "FATAL ERROR: JavaScript heap out of memory" عند محاولة تحليل ملف JSON كبير؟ أنت لست وحدك. يُعد التعامل مع ملفات JSON الكبيرة (100MB+) أحد أكثر التحديات شيوعًا في تطوير الويب الحديث.

في هذا الدليل الشامل، ستتعلم تقنيات مثبتة لتحليل ملفات JSON الكبيرة بكفاءة دون استنزاف الذاكرة أو تعطل التطبيق.

لماذا يفشل JSON.parse() مع الملفات الكبيرة

الطريقة القياسية JSON.parse() تحمل الملف بأكمله في الذاكرة دفعة واحدة:

// ⚠️ سيء للملفات الكبيرة (>100MB)

const fs = require('fs');

const data = fs.readFileSync('huge-file.json', 'utf8');

const json = JSON.parse(data); // يستهلك الكثير من الذاكرة!

لماذا يفشل هذا:
  • يخزن السلسلة النصية بأكملها في الذاكرة (2-3× حجم الملف)
  • ينشئ كائن JavaScript كامل (إضافة 2-5× حجم الملف)
  • لا يوجد تحرير للذاكرة حتى الانتهاء
  • الإجمالي: ملف 500MB يمكن أن يستخدم أكثر من 2GB من الذاكرة!

الحل 1: البث باستخدام JSONStream (Node.js)

أفضل نهج لملفات JSON الكبيرة هو البث - معالجة البيانات قطعة قطعة.

مثال عملي

const fs = require('fs');

const JSONStream = require('JSONStream');

// تحليل مصفوفة من الكائنات

const stream = fs.createReadStream('large-dataset.json', { encoding: 'utf8' });

const parser = JSONStream.parse(''); // '' لعناصر المصفوفة

let count = 0;

parser.on('data', (item) => {

// معالجة عنصر واحد في كل مرة

console.log(معالجة السجل ${++count}:, item.id);

// أدخل منطقك هنا - إدراج في قاعدة البيانات، تحويل، إلخ

if (item.price > 1000) {

saveToDatabase(item);

}

});

parser.on('end', () => {

console.log(تمت معالجة ${count} سجل بنجاح);

});

parser.on('error', (err) => {

console.error('خطأ في التحليل:', err);

});

stream.pipe(parser);

تحليل مسارات JSON متداخلة

// لـ: { "users": [ {...}, {...} ] }

const parser = JSONStream.parse('users.');

// لـ: { "data": { "items": [...] } }

const parser = JSONStream.parse('data.items.');

// لـ: هياكل عميقة أو شروط معقدة

const parser = JSONStream.parse(['data', 'nested', { emitKey: true }]);

مقارنة الأداء

| الطريقة | حجم الملف | استخدام الذاكرة | الوقت |

|---------|-----------|-----------------|-------|

| JSON.parse() | 500MB | ~2.1GB 💥 | 8 ثانية |

| JSONStream | 500MB | ~45MB ✅ | 12 ثانية |

| stream-json | 500MB | ~38MB ✅ | 10 ثانية |

المعايير: Node.js 20، ماك M1، 16GB RAM

الحل 2: stream-json (أسرع)

stream-json أسرع من JSONStream ويوفر المزيد من التحكم:
const { chain } = require('stream-chain');

const { parser } = require('stream-json');

const { streamArray } = require('stream-json/streamers/StreamArray');

const pipeline = chain([

fs.createReadStream('huge-array.json'),

parser(),

streamArray()

]);

pipeline.on('data', ({ key, value }) => {

console.log(الفهرس ${key}:, value.name);

// المعالجة هنا

processRecord(value);

});

pipeline.on('end', () => console.log('تم!'));

لكائنات JSON (وليس مصفوفات)

const { streamObject } = require('stream-json/streamers/StreamObject');

const pipeline = chain([

fs.createReadStream('huge-object.json'),

parser(),

streamObject()

]);

pipeline.on('data', ({ key, value }) => {

console.log(المفتاح: ${key}, value);

});

الحل 3: القراءة المقسمة (المتصفح)

في المتصفحات، استخدم FileReader لمعالجة الملفات بشكل تدريجي:

async function parseHugeJSON(file) {

const chunkSize = 1024 1024; // 1MB

let offset = 0;

let buffer = '';

let results = [];

while (offset < file.size) {

const chunk = file.slice(offset, offset + chunkSize);

const text = await chunk.text();

buffer += text;

// محاولة تحليل الأشياء الكاملة

const lines = buffer.split('\n');

buffer = lines.pop(); // احتفظ بالسطر غير المكتمل

for (const line of lines) {

if (line.trim()) {

try {

const obj = JSON.parse(line);

results.push(obj);

// معالجة على دفعات لتجنب تراكم الذاكرة

if (results.length >= 1000) {

await processBatch(results);

results = [];

}

} catch (e) {

console.warn('تخطي سطر غير صالح:', line.substring(0, 50));

}

}

}

offset += chunkSize;

// الإبلاغ عن التقدم

const progress = (offset / file.size 100).toFixed(1);

console.log(التقدم: ${progress}%);

}

// معالجة الدفعة الأخيرة

if (results.length > 0) {

await processBatch(results);

}

}

// الاستخدام

document.getElementById('fileInput').addEventListener('change', async (e) => {

const file = e.target.files[0];

await parseHugeJSON(file);

});

الحل 4: Web Workers (المعالجة غير المحظورة)

لمنع تجميد واجهة المستخدم، انقل التحليل إلى Web Worker:

// worker.js

self.onmessage = async function(e) {

const { file } = e.data;

const chunkSize = 1024 1024;

let processed = 0;

for (let offset = 0; offset < file.size; offset += chunkSize) {

const chunk = file.slice(offset, offset + chunkSize);

const text = await chunk.text();

// معالجة القطعة

const lines = text.split('\n').filter(l => l.trim());

const objects = lines.map(line => {

try {

return JSON.parse(line);

} catch {

return null;

}

}).filter(Boolean);

// إرسال النتائج مرة أخرى

self.postMessage({

type: 'progress',

data: objects,

percent: (offset / file.size 100)

});

processed += objects.length;

}

self.postMessage({ type: 'complete', total: processed });

};

// main.js

const worker = new Worker('worker.js');

worker.onmessage = function(e) {

if (e.data.type === 'progress') {

console.log(معالج: ${e.data.percent.toFixed(1)}%);

updateUI(e.data.data);

} else if (e.data.type === 'complete') {

console.log(اكتمل! الإجمالي: ${e.data.total});

}

};

// بدء المعالجة

const fileInput = document.getElementById('upload');

fileInput.addEventListener('change', (e) => {

worker.postMessage({ file: e.target.files[0] });

});

الحل 5: التقسيم إلى صفحات للـ API

إذا كنت تجلب JSON من API، استخدم التقسيم إلى صفحات:

async function fetchAllData(apiUrl) {

let allData = [];

let page = 1;

let hasMore = true;

while (hasMore) {

const response = await fetch(${apiUrl}?page=${page}&limit=100);

const data = await response.json();

// معالجة الصفحة على الفور

await processPage(data.items);

allData.push(...data.items);

hasMore = data.hasNextPage;

page++;

// إضافة تأخير لتجنب تحديد المعدل

await new Promise(resolve => setTimeout(resolve, 100));

}

return allData;

}

// نسخة محسّنة: لا تخزن كل شيء

async function processAllData(apiUrl) {

let page = 1;

let hasMore = true;

let totalProcessed = 0;

while (hasMore) {

const response = await fetch(${apiUrl}?page=${page}&limit=100);

const data = await response.json();

// معالجة بدون تخزين

for (const item of data.items) {

await processItem(item); // حفظ في DB، تحويل، إلخ

totalProcessed++;

}

hasMore = data.hasNextPage;

page++;

console.log(معالج ${totalProcessed} عنصر حتى الآن...);

}

return totalProcessed;

}

الحل 6: Python مع ijson

لمعالجة من جانب الخادم، Python's ijson ممتاز:

import ijson

def process_large_json(filename):

with open(filename, 'rb') as file:

# تحليل مصفوفة من الكائنات

objects = ijson.items(file, 'item')

count = 0

for obj in objects:

# معالجة كل عنصر

if obj.get('price', 0) > 1000:

save_to_database(obj)

count += 1

if count % 1000 == 0:

print(f"معالج {count} سجل...")

print(f"إجمالي: {count} سجل")

# لهياكل متداخلة

def process_nested(filename):

with open(filename, 'rb') as file:

# استخرج مفاتيح معينة فقط

for user_id in ijson.items(file, 'users.item.id'):

print(f"معرف المستخدم: {user_id}")

استراتيجيات التحسين

1. التجميع بدلاً من المعالجة الفردية

let batch = [];

const BATCH_SIZE = 1000;

parser.on('data', async (item) => {

batch.push(item);

if (batch.length >= BATCH_SIZE) {

await insertBatch(batch); // إدراج دفعة واحدة

batch = [];

}

});

parser.on('end', async () => {

if (batch.length > 0) {

await insertBatch(batch); // معالجة الدفعة الأخيرة

}

});

2. تصفية البيانات مبكرًا

const { pick } = require('stream-json/filters/Pick');

const { streamArray } = require('stream-json/streamers/StreamArray');

const pipeline = chain([

fs.createReadStream('data.json'),

parser(),

pick({ filter: 'users' }), // فقط خاصية "users"

streamArray()

]);

3. ضغط الملفات

const zlib = require('zlib');

const pipeline = chain([

fs.createReadStream('data.json.gz'),

zlib.createGunzip(), // فك الضغط أثناء الطيران

parser(),

streamArray()

]);

السيناريوهات الشائعة وحلولها

السيناريو 1: استيراد قاعدة البيانات من JSON

const { chain } = require('stream-chain');

const { parser } = require('stream-json');

const { streamArray } = require('stream-json/streamers/StreamArray');

const { MongoClient } = require('mongodb');

async function importToMongo(filename, collectionName) {

const client = await MongoClient.connect('mongodb://localhost:27017');

const collection = client.db('mydb').collection(collectionName);

let batch = [];

const BATCH_SIZE = 1000;

const pipeline = chain([

fs.createReadStream(filename),

parser(),

streamArray()

]);

for await (const { value } of pipeline) {

batch.push(value);

if (batch.length >= BATCH_SIZE) {

await collection.insertMany(batch);

console.log(أدرج ${batch.length} وثائق);

batch = [];

}

}

if (batch.length > 0) {

await collection.insertMany(batch);

}

await client.close();

}

السيناريو 2: تحويل JSON إلى CSV

const { Transform } = require('stream');

const csvWriter = require('csv-write-stream');

const jsonToCSV = new Transform({

objectMode: true,

transform(chunk, encoding, callback) {

// تحويل كائن JSON إلى صف CSV

callback(null, {

id: chunk.id,

name: chunk.name،

email: chunk.email

});

}

});

const writer = csvWriter();

writer.pipe(fs.createWriteStream('output.csv'));

pipeline

.pipe(jsonToCSV)

.pipe(writer);

السيناريو 3: معالجة ملف JSON متعدد الأسطر (NDJSON)

const readline = require('readline');

async function processNDJSON(filename) {

const fileStream = fs.createReadStream(filename);

const rl = readline.createInterface({

input: fileStream,

crlfDelay: Infinity

});

let count = 0;

for await (const line of rl) {

if (line.trim()) {

const obj = JSON.parse(line);

await processObject(obj);

count++;

}

}

console.log(معالج ${count} سطر);

}

استكشاف الأخطاء وإصلاحها

خطأ: JavaScript heap out of memory

الحل:
# زيادة حد الذاكرة في Node.js

node --max-old-space-size=4096 script.js

لكن الأفضل: استخدم البث بدلاً من ذلك!

خطأ: JSON غير صالح عند القراءة المقسمة

المشكلة: القطع قد تقسم كائن JSON. الحل: استخدم JSON محدد بأسطر (NDJSON):
// تحويل JSON إلى NDJSON أولاً

const items = require('./data.json'); // تحميل صغير مرة واحدة

const stream = fs.createWriteStream('data.ndjson');

items.forEach(item => {

stream.write(JSON.stringify(item) + '\n');

});

stream.end();

خطأ: العملية بطيئة جدًا

التحسينات:
  • استخدم stream-json بدلاً من JSONStream (أسرع 2×)
  • زيادة حجم الدفعة (1000 → 5000)
  • موازاة الإدراج في قاعدة البيانات
  • استخدم تجمعات اتصال قاعدة البيانات
  • إضافة فهارس قبل الإدراج الجماعي
  • الأدوات الموصى بها

    للتحقق من صحة JSON

    استخدم BigJSON.online للتحقق بسرعة من صحة ملفات JSON الكبيرة وفحصها دون تحميلها كليًا. يدعم البث والتحقق في الوقت الفعلي.

    المكتبات

    • Node.js: stream-json (الأسرع)، JSONStream (الأبسط)
    • Python: ijson (الأفضل)
    • المتصفح: FileReader API + Web Workers

    الخلاصة

    توصيات سريعة:

    | حالة الاستخدام | أفضل حل | استخدام الذاكرة |

    |----------------|---------|-----------------|

    | Node.js (مصفوفات كبيرة) | stream-json | منخفض جدًا |

    | Node.js (هياكل بسيطة) | JSONStream | منخفض |

    | المتصفح (< 50MB) | القراءة المقسمة | متوسط |

    | المتصفح (> 50MB) | Web Workers | منخفض |

    | تنزيل API | التقسيم + معالجة بث | منخفض جدًا |

    | Python | ijson | منخفض جدًا |

    النقاط الرئيسية:
    • استخدم البث دائمًا للملفات > 50MB
    • المعالجة على دفعات للإدراج في قاعدة البيانات
    • تصفية مبكرًا لتقليل استخدام الذاكرة
    • لا تستخدم JSON.parse() للملفات الكبيرة
    • لا تحمل كل شيء في الذاكرة دفعة واحدة

    استخدم حزم البث الحديثة، وستتمكن من معالجة ملفات JSON بحجم GB بسهولة دون استنزاف الذاكرة أو التعطل.

    موارد إضافية

    هل وجدت هذا الدليل مفيدًا؟ شاركه مع زملائك المطورين الذين يعانون من ملفات JSON الكبيرة!

    Share:

    مقالات ذات صلة

    Read in English