Lire efficacement des fichiers en Node.js avec le module fs
Utilisez readFile() de node:fs/promises lorsque vous avez besoin du contenu complet d’un petit fichier. Pour un gros
fichier, traitez un flux un fragment à la fois. Les exemples ci-dessous lisent la même entrée comme
du texte, comptent ses octets et inspectent ses dix premiers octets sans modifier le fichier.
Préparer un fichier d’exemple
Ces exemples ont été testés avec Node.js v26.8.2 sur macOS. La préparation utilise Bash ; aucun
paquet n’est à installer. Enregistrez les exemples JavaScript sous les noms de fichiers .mjs
indiqués afin que Node.js les charge comme modules ES,
y compris dans un projet CommonJS.
Exécutez ceci dans un répertoire où vous souhaitez créer la démo :
mkdir node-read-demo &&
cd node-read-demo &&
printf 'Hello, 🌍!\n' > example.txt
La préparation refuse de réutiliser un répertoire node-read-demo existant. Si mkdir ou cd échoue, la
chaîne && s’arrête avant d’écrire example.txt. En cas d’échec, arrêtez-vous et choisissez un nouvel
emplacement. Une fois la préparation réussie, restez dans node-read-demo et enregistrez-y chaque script.
Les scripts se contentent d’afficher des résultats dans le terminal ; les relancer laisse votre
entrée inchangée.
Lire un petit fichier UTF-8
Enregistrez ce script sous le nom read-text.mjs :
import { readFile } from 'node:fs/promises'
async function main() {
const path = process.argv[2] ?? 'example.txt'
const text = await readFile(path, 'utf8')
process.stdout.write(text)
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
Exécutez-le depuis le répertoire de démo :
node read-text.mjs example.txt
La sortie est Hello, 🌍! suivi d’un saut de ligne, exactement tel qu’enregistré dans le fichier.
L’argument facultatif vaut example.txt par défaut. Les chemins d’entrée relatifs sont résolus à partir
du répertoire de travail courant du terminal, et non du répertoire du script.
L’argument 'utf8' fait que readFile()
renvoie une chaîne. Omettez-le lorsque vous avez besoin d’un Buffer contenant les octets d’origine,
par exemple pour une image ou une archive. Le décodage UTF-8 est destiné au texte, pas aux données
binaires arbitraires.
Traiter un gros fichier sans l’accumuler en mémoire
Bien que readFile() soit asynchrone, cette fonction charge tout de même l’intégralité du résultat en
mémoire. Utilisez-la lorsque cela respecte le budget mémoire de votre application, lectures
simultanées comprises. Un Promise.all() sur une liste non bornée de fichiers peut conserver de nombreux
résultats complets en même temps.
Un flux vous permet de consommer puis de libérer les fragments. Enregistrez ce script sous le nom
count-bytes.mjs pour compter les octets réellement lus :
import { createReadStream } from 'node:fs'
async function main() {
const path = process.argv[2] ?? 'example.txt'
let total = 0
for await (const chunk of createReadStream(path)) {
total += chunk.length
}
console.log(`Read ${total} bytes.`)
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
node count-bytes.mjs example.txt
Ce script affiche Read 13 bytes. Le globe occupe quatre octets UTF-8 ; compter les caractères donnerait
donc un résultat différent. Aucun encodage n’est défini sur le flux : chaque fragment est un
Buffer d’octets bruts. Un fichier vide affiche Read 0 bytes.
La boucle for await...of consomme
le flux et propage les échecs de lecture à catch. Par défaut, le flux de fichier ferme son
descripteur à la fin de la lecture ou en cas d’erreur. Cet exemple conserve un compteur plutôt que
tous les fragments ; ajouter chaque fragment à un tableau ou à une chaîne ferait perdre cet avantage
en mémoire.
Pour adapter la boucle à un traitement séquentiel, effectuez le travail à l’intérieur de celle-ci et
attendez le travail asynchrone avec await avant de continuer. Un fragment n’est pas nécessairement
une ligne complète, un objet JSON ou un enregistrement CSV. Utilisez un parseur qui gère ces limites
si votre tâche a besoin de ces enregistrements. Si vous n’avez besoin que de la taille déclarée d’un
fichier, stat() évite complètement de lire son contenu.
Lire uniquement les dix premiers octets
Pour inspecter un en-tête binaire, ouvrez un FileHandle et lisez un préfixe borné. Enregistrez ce script
sous le nom read-prefix.mjs :
import { open } from 'node:fs/promises'
async function main() {
const path = process.argv[2] ?? 'example.txt'
const handle = await open(path, 'r')
try {
const buffer = Buffer.alloc(10)
let total = 0
while (total < buffer.length) {
const { bytesRead } = await handle.read(buffer, total, buffer.length - total, total)
if (bytesRead === 0) break
total += bytesRead
}
console.log(`Read ${total} bytes: ${buffer.subarray(0, total).toString('hex')}`)
} finally {
await handle.close()
}
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
node read-prefix.mjs example.txt
Ce script affiche Read 10 bytes: 48656c6c6f2c20f09f8c. Le dernier argument de
handle.read() est la
position dans le fichier ; ici, elle progresse à partir de zéro. Une lecture peut renvoyer moins
d’octets que demandé ; la boucle continue donc jusqu’à obtenir dix octets ou atteindre la fin du
fichier (EOF, bytesRead === 0). finally ferme le handle de fichier même si la lecture échoue.
L’appel subarray() limite la sortie
aux octets réellement lus, en excluant l’espace inutilisé pour un fichier court ou vide. La sortie
hexadécimale évite aussi de décoder un caractère UTF-8 incomplet : ce préfixe de dix octets
s’arrête au milieu du globe.
Diagnostiquer un échec de lecture
Essayez un nom de fichier qui n’existe pas :
node read-text.mjs missing.txt
Cette commande affiche Read failed: ENOENT sur la sortie d’erreur standard et se termine avec le code de sortie
1. Les trois scripts signalent les échecs de lecture de cette manière. Définir
process.exitCode signale l’échec tout
en laissant la sortie en attente se terminer.
Pour ENOENT, vérifiez le chemin d’entrée et le répertoire de travail. EACCES indique un problème
de permissions ; vérifiez que le processus peut traverser les répertoires parents et lire le
fichier. Tentez directement la lecture au lieu de vérifier d’abord avec access() : Node.js documente
la situation de concurrence entre la vérification et l’ouverture.
Adapter l’API au code existant
La forme à callback de fs.readFile()
est toujours prise en charge. Dans du code basé sur des callbacks, inspectez le premier argument
error avant d’utiliser les données ; vous n’avez pas besoin de convertir le code environnant en
promesses uniquement pour une lecture.
fs.readFileSync() bloque l’exécution JavaScript
jusqu’à la fin de la lecture. Cette méthode peut convenir à un court script en ligne de commande ou
au chargement d’une configuration au démarrage. N’utilisez pas de lectures bloquantes dans les
gestionnaires de requêtes qui doivent servir d’autres clients pendant que des E/S disque sont en
attente.
