← Retour au Blog

Comment Parser de Gros Fichiers JSON Sans Plantage en 2026

Guide complet sur la gestion de fichiers JSON volumineux (100MB+) en JavaScript et Python. Apprenez les techniques de streaming, parsing progressif et découpage pour éviter les erreurs de mémoire.

Big JSON Team15 min de lectureprogramming
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 min de lecture

# Comment Parser de Gros Fichiers JSON Sans Planter en 2026

Introduction

Avez-vous déjà rencontré l'erreur "FATAL ERROR: JavaScript heap out of memory" en essayant de parser un gros fichier JSON ? Vous n'êtes pas seul. La gestion de fichiers JSON volumineux (100MB+) est l'un des défis les plus courants du développement web moderne.

Dans ce guide complet, vous apprendrez des techniques éprouvées pour parser efficacement de gros fichiers JSON sans épuiser la mémoire ni faire planter votre application.

Pourquoi JSON.parse() Échoue avec les Gros Fichiers

La méthode standard JSON.parse() charge tout le fichier en mémoire d'un coup :

// ⚠️ Mauvais pour les gros fichiers (>100MB)

const fs = require('fs');

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

const json = JSON.parse(data); // Consomme énormément de mémoire !

Pourquoi ça échoue :
  • Stocke toute la chaîne en mémoire (2-3× taille du fichier)
  • Crée l'objet JavaScript complet (encore 2-5× taille du fichier)
  • Aucune libération de mémoire jusqu'à la fin
  • Total : Un fichier de 500MB peut utiliser plus de 2GB de mémoire !

Solution 1 : Streaming avec JSONStream (Node.js)

La meilleure approche pour les gros fichiers JSON est le streaming - traiter les données morceau par morceau.

Exemple Pratique

const fs = require('fs');

const JSONStream = require('JSONStream');

// Parser un tableau d'objets

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

const parser = JSONStream.parse(''); // '' pour les éléments du tableau

let count = 0;

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

// Traiter un élément à la fois

console.log(Traitement de l'enregistrement ${++count}:, item.id);

// Votre logique ici - insérer en BD, transformer, etc.

if (item.price > 1000) {

saveToDatabase(item);

}

});

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

console.log(Traité avec succès ${count} enregistrements);

});

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

console.error('Erreur de parsing:', err);

});

stream.pipe(parser);

Parser des Chemins JSON Imbriqués

// Pour : { "users": [ {...}, {...} ] }

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

// Pour : { "data": { "items": [...] } }

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

// Pour : structures profondes ou conditions complexes

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

Comparaison de Performance

| Méthode | Taille Fichier | Utilisation Mémoire | Temps |

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

| JSON.parse() | 500MB | ~2.1GB 💥 | 8s |

| JSONStream | 500MB | ~45MB ✅ | 12s |

| stream-json | 500MB | ~38MB ✅ | 10s |

Benchmark : Node.js 20, Mac M1, 16GB RAM

Solution 2 : stream-json (Plus Rapide)

stream-json est plus rapide que JSONStream et offre plus de contrôle :
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(Index ${key}:, value.name);

// Traiter ici

processRecord(value);

});

pipeline.on('end', () => console.log('Terminé !'));

Pour des Objets JSON (Pas de Tableaux)

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

const pipeline = chain([

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

parser(),

streamObject()

]);

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

console.log(Clé : ${key}, value);

});

Solution 3 : Lecture par Morceaux (Navigateur)

Dans les navigateurs, utilisez FileReader pour traiter les fichiers progressivement :

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;

// Essayer de parser des objets complets

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

buffer = lines.pop(); // Garder la ligne incomplète

for (const line of lines) {

if (line.trim()) {

try {

const obj = JSON.parse(line);

results.push(obj);

// Traiter par lots pour éviter l'accumulation de mémoire

if (results.length >= 1000) {

await processBatch(results);

results = [];

}

} catch (e) {

console.warn('Ligne invalide ignorée:', line.substring(0, 50));

}

}

}

offset += chunkSize;

// Signaler la progression

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

console.log(Progression : ${progress}%);

}

// Traiter le dernier lot

if (results.length > 0) {

await processBatch(results);

}

}

// Utilisation

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

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

await parseHugeJSON(file);

});

Solution 4 : Web Workers (Non-Bloquant)

Pour éviter que l'UI ne gèle, déplacez le parsing dans un 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();

// Traiter le morceau

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

const objects = lines.map(line => {

try {

return JSON.parse(line);

} catch {

return null;

}

}).filter(Boolean);

// Renvoyer les résultats

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(Traité : ${e.data.percent.toFixed(1)}%);

updateUI(e.data.data);

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

console.log(Terminé ! Total : ${e.data.total});

}

};

// Démarrer le traitement

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

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

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

});

Solution 5 : Pagination d'API

Si vous récupérez du JSON depuis une API, utilisez la pagination :

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();

// Traiter la page immédiatement

await processPage(data.items);

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

hasMore = data.hasNextPage;

page++;

// Ajouter un délai pour éviter la limitation de débit

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

}

return allData;

}

// Version optimisée : Ne pas tout stocker

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();

// Traiter sans stocker

for (const item of data.items) {

await processItem(item); // Sauvegarder en BD, transformer, etc.

totalProcessed++;

}

hasMore = data.hasNextPage;

page++;

console.log(Traité ${totalProcessed} éléments jusqu'à présent...);

}

return totalProcessed;

}

Solution 6 : Python avec ijson

Pour le traitement côté serveur, Python's ijson est excellent :

import ijson

def process_large_json(filename):

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

# Parser un tableau d'objets

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

count = 0

for obj in objects:

# Traiter chaque élément

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

save_to_database(obj)

count += 1

if count % 1000 == 0:

print(f"Traité {count} enregistrements...")

print(f"Total : {count} enregistrements")

# Pour des structures imbriquées

def process_nested(filename):

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

# Extraire seulement des clés spécifiques

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

print(f"ID utilisateur : {user_id}")

Stratégies d'Optimisation

1. Traitement par Lots au Lieu d'Individuel

let batch = [];

const BATCH_SIZE = 1000;

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

batch.push(item);

if (batch.length >= BATCH_SIZE) {

await insertBatch(batch); // Une insertion par lot

batch = [];

}

});

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

if (batch.length > 0) {

await insertBatch(batch); // Traiter le dernier lot

}

});

2. Filtrer les Données en Amont

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' }), // Seulement la propriété "users"

streamArray()

]);

3. Compression de Fichiers

const zlib = require('zlib');

const pipeline = chain([

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

zlib.createGunzip(), // Décompresser à la volée

parser(),

streamArray()

]);

Scénarios Courants et Solutions

Scénario 1 : Importation de Base de Données depuis 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(Inséré ${batch.length} documents);

batch = [];

}

}

if (batch.length > 0) {

await collection.insertMany(batch);

}

await client.close();

}

Scénario 2 : Convertir JSON en CSV

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

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

const jsonToCSV = new Transform({

objectMode: true,

transform(chunk, encoding, callback) {

// Convertir objet JSON en ligne 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);

Scénario 3 : Traiter du JSON Délimité par Lignes (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(Traité ${count} lignes);

}

Dépannage

Erreur : JavaScript heap out of memory

Solution :
# Augmenter la limite de mémoire dans Node.js

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

Mais mieux : Utilisez le streaming à la place !

Erreur : JSON Invalide lors de la Lecture par Morceaux

Problème : Les morceaux peuvent diviser les objets JSON. Solution : Utilisez du JSON délimité par lignes (NDJSON) :
// Convertir d'abord JSON en NDJSON

const items = require('./data.json'); // Charger petit une fois

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

items.forEach(item => {

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

});

stream.end();

Erreur : Processus Trop Lent

Optimisations :
  • Utilisez stream-json au lieu de JSONStream (2× plus rapide)
  • Augmenter la taille du lot (1000 → 5000)
  • Paralléliser les insertions en BD
  • Utiliser des pools de connexion BD
  • Ajouter des index avant l'insertion en masse
  • Outils Recommandés

    Pour la Validation JSON

    Utilisez BigJSON.online pour valider et inspecter rapidement de gros fichiers JSON sans les charger complètement. Prend en charge le streaming et la validation en temps réel.

    Bibliothèques

    • Node.js : stream-json (le plus rapide), JSONStream (le plus simple)
    • Python : ijson (le meilleur)
    • Navigateur : FileReader API + Web Workers

    Conclusion

    Recommandations Rapides :

    | Cas d'Usage | Meilleure Solution | Utilisation Mémoire |

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

    | Node.js (gros tableaux) | stream-json | Très faible |

    | Node.js (structures simples) | JSONStream | Faible |

    | Navigateur (< 50MB) | Lecture par morceaux | Moyen |

    | Navigateur (> 50MB) | Web Workers | Faible |

    | Téléchargement API | Pagination + traitement streaming | Très faible |

    | Python | ijson | Très faible |

    Points Clés :
    • Toujours utiliser le streaming pour les fichiers > 50MB
    • Traitement par lots pour les insertions en BD
    • Filtrer en amont pour réduire l'utilisation de la mémoire
    • Ne pas utiliser JSON.parse() pour les gros fichiers
    • Ne pas tout charger d'un coup en mémoire

    Utilisez des paquets de streaming modernes et vous pourrez gérer des fichiers JSON de taille GB facilement sans épuiser la mémoire ni planter.

    Ressources Supplémentaires

    Vous avez trouvé ce guide utile ? Partagez-le avec vos collègues développeurs qui luttent avec de gros fichiers JSON !

    Share:

    Articles Connexes

    Read in English