Node.js I/O

Système de fichiers Node.js

Le module fs permet de lire, d'écrire et de surveiller des fichiers. Utilisez l'API promise, gérez les chemins en toute sécurité et utilisez les streams pour tout contenu volumineux.

intermediate14 min readUpdated 15 sept. 2026
config.js
js
// config.js
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";

const file = path.join(import.meta.dirname, "config.json");

const config = JSON.parse(await readFile(file, "utf8"));
config.updatedAt = new Date().toISOString();

await writeFile(file, JSON.stringify(config, null, 2));
Module
node:fs/promises
Chemins
node:path
Encodage
utf8 pour le texte
Répertoires
mkdir, readdir, rm
Fichiers volumineux
Streams
Sécurité
Protection contre le path traversal

Pourquoi c'est important

Pourquoi la gestion des fichiers est cruciale

Stockage persistant

Les fichiers conservent la configuration, les uploads, les logs et les sorties générées qui doivent survivre à un redémarrage.

L'importance de la sécurité

Les chemins non approuvés et les lectures non limitées sont deux des vulnérabilités côté serveur les plus courantes.

Le streaming stabilise la mémoire

Utilisez des streams pour les fichiers volumineux au lieu de les mettre en mémoire tampon, afin que la consommation mémoire ne croisse pas avec la taille du fichier.

Le tableau complet

Les trois piliers du travail sur les fichiers

Lire et écrire du contenu, gérer les chemins et les répertoires en toute sécurité, et utiliser les streams pour les données volumineuses.

Contenu

Lire et écrire

Lire, écrire et ajouter du contenu aux fichiers, avec un encodage pour le texte.

Chemins

Localiser

Le module path permet de joindre, résoudre et normaliser les chemins sur différentes plateformes.

Streaming

Passer à l'échelle

Utilisez les streams pour les fichiers volumineux et les pipelines pour le traitement des données.

Le système de fichiers en un coup d'œil

Les API fondamentales

readFile et writeFile

Lire et écrire des fichiers complets sous forme de chaîne de caractères ou de buffer.

mkdir et readdir

Créer et lister les répertoires.

module path

join, resolve, basename, extname et normalize.

stat

Vérifier l'existence, la taille, le type et les horodatages.

watch

Réagir aux modifications de fichiers pour les serveurs de dev et l'outillage.

Streams

createReadStream et createWriteStream pour les données volumineuses.

Un bref aperçu

De fs avec callbacks vers l'I/O basé sur les promises

  1. 2009

    fs avec Callbacks

    Node expose l'accès aux fichiers via des callbacks (error-first).

    09
  2. 2014

    Proposition de fs.promises

    Une API basée sur les promises est conçue pour le JavaScript moderne.

    14
  3. 2019

    fs/promises stable

    Les méthodes basées sur les promises deviennent la manière recommandée d'utiliser fs.

    19
  4. 2023

    Support des Globs

    L'intégration native des patterns glob élimine une dépendance courante.

    23
  5. Aujourd'hui

    Priorité aux Promises

    L'accès asynchrone aux fichiers est la norme ; les API sync sont réservées aux scripts et au démarrage.

    Aujourd'hui

Le guide complet

Système de fichiers Node.js: Tout ce que vous devez savoir

Pourquoi la gestion des fichiers est cruciale

Les serveurs lisent et écrivent des fichiers en permanence : configurations, uploads, logs, caches, rapports générés et sorties de build. Le module node:fs de Node.js en est l’interface, et la manière dont vous l’utilisez détermine si votre serveur reste réactif et si vos données sont sécurisées.

Trois points sont essentiels. Utilisez l’API de promesses pour ne pas bloquer l’event loop. Construisez vos chemins de fichiers de manière sécurisée pour éviter que les entrées utilisateur ne puissent sortir d’un répertoire. Et utilisez les streams pour les fichiers volumineux afin que la consommation mémoire ne grimpe pas avec la taille du fichier.

Lire et écrire des fichiers

L’API basée sur les promesses est le standard pour le code applicatif.

// files.js
import { readFile, writeFile, appendFile } from "node:fs/promises";

// text
const text = await readFile("notes.md", "utf8");
await writeFile("notes.md", text + "\nnew line\n", "utf8");
await appendFile("app.log", "event\n", "utf8");

// binary
const image = await readFile("photo.png"); // Buffer
await writeFile("copy.png", image);

Passez un encodage tel que "utf8" pour obtenir une chaîne de caractères ; omettez-le pour obtenir un Buffer d’octets bruts. writeFile remplace le fichier, tandis que appendFile ajoute du contenu à celui-ci. Aucun des deux ne crée les répertoires manquants, appelez donc mkdir au préalable si nécessaire.

Répertoires

// dirs.js
import { mkdir, readdir, rm, rename, stat } from "node:fs/promises";

await mkdir("data/cache", { recursive: true });

const entries = await readdir("data", { withFileTypes: true });
for (const entry of entries) {
  if (entry.isDirectory()) console.log("dir", entry.name);
}

const info = await stat("data/cache");
console.log(info.size, info.mtime);

await rename("data/tmp.json", "data/final.json");
await rm("data/old", { recursive: true, force: true });

recursive: true permet à mkdir et rm d’opérer sur des arbres complets, et withFileTypes renvoie des entrées que vous pouvez tester sans appels stat supplémentaires. Utilisez stat pour vérifier l’existence, la taille et les horodatages, mais gardez à l’esprit qu’il suit les liens symboliques ; lstat ne le fait pas.

Chemins (Paths)

Le module node:path gère la manipulation des chemins de manière portable.

// paths.js
import path from "node:path";

const file = path.join("uploads", "avatars", "ada.png");
const absolute = path.resolve("uploads", "ada.png");
const name = path.basename(file);          // ada.png
const ext = path.extname(file);            // .png
const dir = path.dirname(file);            // uploads/avatars

Utilisez path.join et path.resolve plutôt que la concaténation de chaînes, et import.meta.dirname (ou import.meta.url sur les versions plus anciennes) pour localiser des fichiers relativement au module actuel. Sous Windows, path utilise des barres obliques inverses (backslashes), c’est pourquoi la concaténation manuelle pose problème.

Sécurité : path traversal

La vulnérabilité de fichier la plus courante consiste à laisser une entrée utilisateur s’échapper du répertoire prévu.

// safe-path.js
import path from "node:path";

const BASE = "/srv/uploads";

function safePath(name) {
  const target = path.resolve(BASE, name);
  if (target !== BASE && !target.startsWith(BASE + path.sep)) {
    throw new Error("Invalid path");
  }
  return target;
}

Résolvez l’entrée par rapport à une base fixe, puis vérifiez que le résultat se trouve toujours à l’intérieur de celle-ci. Validez également les noms de fichiers, rejetez les octets nuls et n’exposez jamais les erreurs brutes du système de fichiers aux clients. Consultez le guide de sécurité Web.

Écritures atomiques

Si un plantage survient en cours d’écriture, un lecteur peut se retrouver avec un fichier partiellement écrit. Pour l’état et la configuration, écrivez dans un fichier temporaire puis renommez-le pour remplacer le fichier original.

// atomic.js
import { writeFile, rename } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const tmp = `/srv/state/.${randomUUID()}.tmp`;
await writeFile(tmp, JSON.stringify(state), "utf8");
await rename(tmp, "/srv/state/current.json");

rename est atomique sur un même système de fichiers, ainsi les lecteurs voient toujours soit l’ancien fichier, soit le nouveau dans son intégralité.

Streaming de fichiers volumineux

Pour tout contenu potentiellement volumineux, privilégiez le streaming plutôt que la mise en mémoire tampon (buffering).

// stream.js
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";

await pipeline(
  createReadStream("big.csv"),
  createWriteStream("copy.csv"),
);

Le streaming permet de maintenir une consommation mémoire stable et vous permet de commencer le traitement avant même que l’intégralité du fichier ne soit lue. Le guide sur les Streams traite des transformations, de la backpressure et du mode objet.

Surveiller les fichiers

// watch.js
import { watch } from "node:fs";

const watcher = watch("./config", { recursive: true }, (event, filename) => {
  console.log(event, filename);
});

process.on("SIGINT", () => watcher.close());

Les watchers sont idéaux pour les serveurs de développement et le rechargement de configurations. Utilisez le debounce pour gérer les rafales d’événements, gérez les suppressions et les renommages, et pensez à toujours fermer le watcher pour permettre au processus de s’arrêter.

Bonnes pratiques

  • Utilisez node:fs/promises dans le code de l’application.
  • Lisez et écrivez avec un encodage explicite, ou attendez-vous à recevoir un Buffer.
  • Construisez vos chemins avec node:path et résolvez-les par rapport à une base connue.
  • Protégez-vous contre les attaques de type “path traversal” sur tout nom de fichier fourni par l’utilisateur.
  • Utilisez des flux (streams) pour les fichiers volumineux ; ne mettez en mémoire tampon (buffer) que ce dont vous avez besoin intégralement.
  • Écrivez les fichiers d’état de manière atomique en utilisant un fichier temporaire puis en le renommant.
  • Fermez les watchers et les descripteurs de fichiers (file handles) pour que les processus puissent s’arrêter proprement.

Erreurs courantes

  • Utiliser readFileSync dans un gestionnaire de requêtes.
  • Concaténer des entrées utilisateur dans des chemins de fichiers.
  • Supposer qu’un répertoire existe avant d’écrire.
  • Lire un fichier volumineux en mémoire, provoquant un plantage lors d’une forte charge.
  • Exposer des erreurs brutes du système de fichiers et divulguer des chemins.
  • Oublier de fermer les watchers ou les streams.

Et après ?

La manipulation de fichiers est une tâche quotidienne côté serveur. Allez plus loin avec les Streams, comprenez l’ordonnancement dans le guide sur l’Event Loop, et sécurisez vos chemins d’accès avec le guide sur la sécurité Web.

Lecture d'un fichier

Utilisez l'API promise dans les serveurs pour laisser l'event loop libre. Les appels sync bloquent toutes les autres requêtes.

À privilégier
import { readFile } from "node:fs/promises";

const text = await readFile("data.json", "utf8");
À éviter
import { readFileSync } from "node:fs";

// blocks the whole server
const text = readFileSync("data.json", "utf8");

Construction d'un chemin

Résolvez l'entrée utilisateur par rapport à une base fixe et rejetez tout ce qui en sort. La concaténation de chaînes favorise le path traversal.

À privilégier
import path from "node:path";

const base = "/srv/uploads";
const target = path.resolve(base, name);

if (!target.startsWith(base + path.sep)) {
  throw new Error("Invalid path");
}
À éviter
// "../../etc/passwd" escapes
// the intended directory
const target = "/srv/uploads/" + name;

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre File System ?

Notre tutoriel interactif vous guide à travers File System pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.