Fichiers et chemins avec fs et path
node:fs/promises et node:path en conditions réelles.
Objectifs
À la fin de cette leçon, vous saurez :
- lire et écrire des fichiers avec node:fs ;
- composer des chemins portables avec node:path ;
- choisir entre versions synchrones, callbacks et promises.
🔗 Pour vous rafraîchir la mémoire : l'event loop
Importer les modules natifs
const fs = require("node:fs"); // style CommonJS (historique)
// ou
import fs from "node:fs"; // style ESM (moderne)
Le préfixe node: explicite « module natif » : adoptez-le systématiquement.
Écrire puis lire
import { writeFileSync, readFileSync } from "node:fs";
writeFileSync("journal.txt", "Première entrée\n");
const contenu = readFileSync("journal.txt", "utf8");
console.log(contenu);
Sans l'encodage "utf8", readFileSync renvoie un Buffer (octets bruts) : c'est la cause classique d'un affichage <Buffer 50 61 ...>.
La version promise : le standard moderne
import { readFile, writeFile } from "node:fs/promises";
async function journaliser(texte) {
const ancien = await readFile("journal.txt", "utf8").catch(() => "");
await writeFile("journal.txt", ancien + texte + "\n");
}
.catch(() => "") absorbe proprement le cas « fichier pas encore créé ». Préférez cette API async partout : elle ne bloque pas l'event loop — vital dès que votre serveur servira plusieurs clients.
Rappel du tableau des trois styles (leçon 17-01) : les versions *Sync de fs sont le style SYNCHRONE — elles gèlent l'event loop pendant la lecture. Acceptables dans un script d'initialisation qui tourne seul ; interdites dans un serveur qui sert plusieurs clients.
Gérer les erreurs réelles
Les opérations fichiers échouent pour des raisons ordinaires — fichier absent, permissions, disque plein. L'API promise rejette une erreur avec un code exploitable :
import { readFile } from "node:fs/promises";
try {
const contenu = await readFile("config.json", "utf8");
console.log(JSON.parse(contenu));
} catch (erreur) {
if (erreur.code === "ENOENT") {
console.log("Pas encore de configuration : valeurs par défaut");
} else if (erreur.code === "EACCES") {
console.error("Droits insuffisants sur config.json");
process.exit(1);
} else {
throw erreur; // inconnue → on laisse remonter
}
}
Les codes à connaître : ENOENT (fichier/dossier inexistant), EACCES/EPERM (permissions), EEXIST (création d'un existant). Lire erreur.code plutôt que le message textuel rend vos scripts robustes quelle que soit la langue du système.
Les chemins avec path
Concaténer des chaînes pour faire des chemins casse sur Windows (\) vs Linux (/). path.join produit le bon séparateur :
import { join } from "node:path";
const dossier = join(__dirname, "donnees");
const fichier = join(dossier, "journal.txt");
// Linux : /home/cours/projet/donnees/journal.txt
// Windows: C:\projet\donnees\journal.txt
Compléments utiles : path.basename(fichier) (nom seul), path.extname(fichier) (extension), mkdirSync(dossier, { recursive: true }) (créer l'arborescence).
Exercice
- Créez un script qui écrit trois lignes dans
courses.txtpuis les relit en majuscules. - Faites une version async de ce script.
- Construisez avec
joinle chemin d'un fichier dans un sous-dossier créé récursivement. - Que se passe-t-il si vous relisez sans
"utf8"?
Résumé
node:fs/promises+ async/await = le duo par défaut.- Toujours préciser
"utf8"pour du texte. path.joinrend les chemins portables ;__dirnameancre au fichier courant.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Question 2.
import { readFile, writeFile } from "node:fs/promises";
await writeFile("courses.txt", "pain\nlait\nœufs\n");
const contenu = await readFile("courses.txt", "utf8");
console.log(contenu.toUpperCase());
Question 4. Vous recevez un Buffer ; .toUpperCase() échoue (méthode inexistante) ou affiche des octets hexadécimaux selon l'usage. Le Buffer reste utile pour les binaires (images) — pour du texte, exigez toujours l'encodage.