Contactez-nous

Lecture de fichiers (synchrone et asynchrone)

Apprenez a lire le contenu de fichiers en Node.js avec les methodes asynchrones (readFile, promises.readFile) et synchrones (readFileSync) du module fs.

Pourquoi et comment lire des fichiers en Node.js ?

La lecture du contenu des fichiers est une opération extrêmement courante en développement back-end. Que ce soit pour charger des fichiers de configuration (JSON, YAML, .env), lire des templates HTML, accéder à des données stockées localement (CSV, logs), ou récupérer le contenu d'un script, savoir lire un fichier est fondamental. Le module `fs` de Node.js nous offre les outils nécessaires pour accomplir cette tâche.

Comme évoqué précédemment, Node.js met l'accent sur les opérations asynchrones non bloquantes pour maximiser les performances. Pour la lecture de fichiers, cela se traduit par une préférence marquée pour les fonctions asynchrones. Cependant, une version synchrone existe également, utile dans des contextes très spécifiques. Nous allons explorer ces deux approches.

Lecture asynchrone : `fs.readFile` et `fs.promises.readFile`

La méthode recommandée pour lire le contenu intégral d'un fichier en mémoire est `fs.readFile()`. Elle est asynchrone, ce qui signifie qu'elle ne bloque pas la boucle d'événements de Node.js pendant que le système d'exploitation lit le fichier sur le disque.

Sa signature de base utilise un callback :

fs.readFile(path, [options], callback)
  • path : Le chemin vers le fichier (chaîne de caractères, Buffer, ou URL `file:`).
  • options (optionnel) : Un objet ou une chaîne de caractères spécifiant notamment l'encodage (par exemple, `{ encoding: 'utf8' }` ou simplement `'utf8'`). Si aucun encodage n'est fourni, la fonction retourne un Buffer (données brutes).
  • callback : Une fonction appelée une fois la lecture terminée ou en cas d'erreur. Elle reçoit deux arguments : (err, data). Si une erreur survient (fichier non trouvé, permissions...), `err` contiendra l'objet d'erreur et `data` sera `undefined`. Si la lecture réussit, `err` sera `null` et `data` contiendra le contenu du fichier (Buffer ou chaîne de caractères selon l'option d'encodage).

Exemple avec callback :

const fs = require('fs');

fs.readFile('config.json', 'utf8', (err, data) => {
  if (err) {
    console.error('Erreur lors de la lecture du fichier:', err);
    // Gérer l'erreur : fichier non trouvé (ENOENT), accès refusé (EACCES), etc.
    return;
  }

  try {
    const config = JSON.parse(data);
    console.log('Configuration chargée:', config);
    // Utiliser l'objet config...
  } catch (parseError) {
    console.error('Erreur lors du parsing JSON:', parseError);
  }
});

console.log('Demande de lecture du fichier initiée...'); // S'affiche avant la fin de la lecture

Node.js propose également une version basée sur les Promesses via `fs.promises` qui s'intègre parfaitement avec `async/await`, rendant le code souvent plus lisible :

const fs = require('fs').promises;

async function readConfigFile() {
  try {
    const data = await fs.readFile('config.json', 'utf8');
    const config = JSON.parse(data);
    console.log('Configuration chargée (async/await):', config);
    // Utiliser l'objet config...
  } catch (err) {
    console.error('Erreur lors de la lecture ou du parsing:', err);
  }
}

console.log('Appel de readConfigFile...');
readConfigFile();
console.log('Demande de lecture initiée (async/await)...');

Avantages de l'asynchrone : Ne bloque pas la boucle d'événements, essentiel pour les serveurs traitant des requêtes multiples. Permet à l'application de rester réactive.

Lecture synchrone : `fs.readFileSync` (a utiliser avec precaution)

Le module `fs` fournit également une contrepartie synchrone : `fs.readFileSync()`. Comme son nom l'indique, cette fonction bloque l'exécution de l'ensemble du processus Node.js jusqu'à ce que la lecture du fichier soit terminée.

Sa signature est plus simple :

fs.readFileSync(path, [options])
  • path et options : Identiques à `fs.readFile`.
  • Retour : Si la lecture réussit, la fonction retourne directement le contenu du fichier (Buffer ou chaîne de caractères).
  • Erreurs : Si une erreur survient pendant la lecture, la fonction lance une exception. Il est donc impératif d'envelopper l'appel dans un bloc `try...catch`.

Exemple avec `try...catch` :

const fs = require('fs');

console.log('Tentative de lecture synchrone...');

try {
  // L'exécution s'arrête ici jusqu'à la fin de la lecture
  const data = fs.readFileSync('config.json', 'utf8');
  const config = JSON.parse(data);
  console.log('Configuration chargée (synchrone):', config);
  // Utiliser l'objet config...
} catch (err) {
  // Si readFileSync ou JSON.parse échoue
  console.error('Erreur lors de la lecture synchrone ou du parsing:', err);
}

console.log('Lecture synchrone terminée (ou erreur capturée).');

Quand utiliser `readFileSync` ? Son usage doit être limité. Il peut être acceptable au tout début du processus, lors de la phase d'initialisation d'une application (par exemple, pour charger une configuration essentielle avant même de démarrer le serveur) ou dans des scripts simples où le blocage de la boucle d'événements n'a pas d'impact négatif (outils en ligne de commande, scripts de build). Evitez-le absolument dans le code destiné à gérer des requêtes web ou d'autres opérations concurrentes, car cela dégraderait gravement les performances et la capacité de votre serveur à répondre à plusieurs utilisateurs.

Points cles et bonnes pratiques

Encodage : N'oubliez pas de spécifier l'encodage (souvent `'utf8'`) si vous lisez un fichier texte et souhaitez obtenir une chaîne de caractères directement. Sans cela, vous recevrez un objet Buffer, que vous devrez ensuite convertir (`buffer.toString('utf8')`).

Gestion des erreurs : La gestion des erreurs est primordiale. Vérifiez toujours le paramètre `err` dans les callbacks ou utilisez `try...catch` avec les versions synchrones et `async/await`. Les erreurs courantes incluent `ENOENT` (fichier non trouvé) et `EACCES` (permissions insuffisantes).

Fichiers volumineux : Les fonctions `fs.readFile` et `fs.readFileSync` chargent l'intégralité du contenu du fichier en mémoire. Pour des fichiers très volumineux (plusieurs mégaoctets ou gigaoctets), cette approche peut saturer la mémoire vive de votre serveur. Dans ces cas, il est impératif d'utiliser les Streams (`fs.createReadStream`) qui permettent de traiter le fichier morceau par morceau, comme nous l'avons vu dans un chapitre précédent. C'est la méthode la plus efficace et la plus économe en ressources pour les gros fichiers.