Back

Écriture de fichiers Node.js expliquée : Tout ce que vous devez savoir sur fs.writeFileSync()

Écriture de fichiers Node.js expliquée : Tout ce que vous devez savoir sur fs.writeFileSync()

Les opérations sur les fichiers constituent une partie fondamentale de nombreuses applications Node.js. Que vous créiez des fichiers de configuration, enregistriez des données ou génériez des rapports, comprendre comment écrire efficacement dans des fichiers est essentiel. La méthode fs.writeFileSync() offre un moyen simple de gérer les opérations d’écriture synchrone de fichiers dans Node.js.

Ce guide complet couvre tout ce que vous devez savoir pour utiliser efficacement fs.writeFileSync(), des usages de base aux techniques avancées et aux meilleures pratiques.

Points clés à retenir

  • fs.writeFileSync() écrit des données dans des fichiers de manière synchrone, bloquant la boucle d’événements jusqu’à la fin de l’opération
  • Utilisez toujours des blocs try-catch pour gérer les erreurs potentielles
  • Utilisez les drapeaux appropriés pour contrôler le comportement de création et d’écriture de fichiers
  • Tenez compte des implications sur les performances dans les applications à forte concurrence
  • Pour les fichiers plus volumineux ou les environnements de production, envisagez des alternatives asynchrones
  • Soyez attentif aux problèmes de sécurité, en particulier avec les chemins fournis par les utilisateurs

Qu’est-ce que fs.writeFileSync() ?

fs.writeFileSync() est une méthode intégrée au module de système de fichiers (fs) de Node.js qui écrit des données dans un fichier de manière synchrone. Contrairement à son homologue asynchrone, cette méthode bloque l’exécution de votre code jusqu’à ce que l’opération sur le fichier soit terminée.

La signature de la méthode est :

fs.writeFileSync(file, data[, options])
  • file : Chemin vers le fichier (chaîne, Buffer, URL ou descripteur de fichier)
  • data : Contenu à écrire (chaîne, Buffer, TypedArray ou DataView)
  • options : Paramètres de configuration optionnels (chaîne ou objet)

Utilisation basique de fs.writeFileSync()

Écriture d’un simple fichier texte

const fs = require('fs');

try {
  fs.writeFileSync('example.txt', 'Hello, Node.js!');
  console.log('Fichier écrit avec succès');
} catch (err) {
  console.error('Erreur lors de l'écriture du fichier:', err);
}

Ce code crée un fichier nommé example.txt avec le contenu ""Hello, Node.js!"" dans votre répertoire de travail actuel.

Écriture de données JSON

const fs = require('fs');

const user = {
  name: 'John Doe',
  age: 30,
  email: 'john@example.com'
};

try {
  fs.writeFileSync('user.json', JSON.stringify(user, null, 2));
  console.log('Fichier JSON écrit avec succès');
} catch (err) {
  console.error('Erreur lors de l'écriture du fichier JSON:', err);
}

Comprendre les paramètres

Le paramètre File

Le premier paramètre spécifie le chemin du fichier où les données seront écrites :

// Écriture dans le répertoire courant
fs.writeFileSync('data.txt', 'Un contenu');

// Écriture dans un chemin spécifique
fs.writeFileSync('/var/logs/app.log', 'Entrée de journal');

// Utilisation d'un descripteur de fichier
const fd = fs.openSync('config.json', 'w');
fs.writeFileSync(fd, '{""setting"": ""value""}');
fs.closeSync(fd);

Important : Si le répertoire spécifié n’existe pas, Node.js générera une erreur. La méthode fs.writeFileSync() ne peut pas créer de répertoires.

Le paramètre Data

Le deuxième paramètre contient le contenu que vous souhaitez écrire :

// Écriture d'une chaîne
fs.writeFileSync('file.txt', 'Contenu texte brut');

// Écriture d'un Buffer
const buffer = Buffer.from('Contenu binaire');
fs.writeFileSync('binary.dat', buffer);

// Écriture d'un TypedArray
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);  // ""Hello"" en ASCII
fs.writeFileSync('typed-array.txt', uint8Array);

Travailler avec les Buffers

Les Buffers sont particulièrement utiles lorsqu’on traite des données binaires :

const fs = require('fs');

// Création d'un buffer à partir d'une chaîne
const stringData = 'Hello World';
const buffer = Buffer.from(stringData, 'utf8');
fs.writeFileSync('buffer-example.txt', buffer);

// Création d'un buffer à partir de JSON
const userData = { name: 'Alice', age: 28 };
const jsonBuffer = Buffer.from(JSON.stringify(userData));
fs.writeFileSync('user-data.json', jsonBuffer);

Le paramètre Options

Le troisième paramètre vous permet de personnaliser le comportement d’écriture du fichier :

fs.writeFileSync('config.txt', 'Données de configuration', {
  encoding: 'utf8',    // Encodage des caractères (par défaut: 'utf8')
  mode: 0o666,         // Permissions du fichier (par défaut: 0o666)
  flag: 'w'            // Drapeau du système de fichiers (par défaut: 'w')
});

Options de drapeaux courantes

  • 'w' : Ouvrir pour écriture, créer si n’existe pas, tronquer si existe (par défaut)
  • 'a' : Ouvrir pour ajout, créer si n’existe pas
  • 'wx' : Comme ‘w’ mais échoue si le chemin existe
  • 'ax' : Comme ‘a’ mais échoue si le chemin existe
  • 'r+' : Ouvrir pour lecture et écriture, le fichier doit exister
// Ajouter à un fichier au lieu de l'écraser
fs.writeFileSync('log.txt', 'Nouvelle entrée de journaln', { flag: 'a' });

// Créer un nouveau fichier seulement s'il n'existe pas
try {
  fs.writeFileSync('config.json', '{}', { flag: 'wx' });
  console.log('Nouveau fichier de configuration créé');
} catch (err) {
  if (err.code === 'EEXIST') {
    console.log('Le fichier de configuration existe déjà');
  } else {
    console.error('Erreur:', err);
  }
}

Gestion des erreurs avec fs.writeFileSync()

Comme fs.writeFileSync() est synchrone, les erreurs sont directement lancées. Encapsulez-la toujours dans un bloc try-catch :

try {
  fs.writeFileSync('/path/to/file.txt', 'Contenu');
} catch (error) {
  // Gérer des types d'erreurs spécifiques
  if (error.code === 'ENOENT') {
    console.error('Le répertoire n'existe pas');
  } else if (error.code === 'EACCES') {
    console.error('Permission refusée');
  } else {
    console.error('Erreur inattendue:', error);
  }
}

Codes d’erreur courants

  • ENOENT : Fichier ou répertoire inexistant (souvent quand le répertoire parent n’existe pas)
  • EACCES : Permission refusée
  • EISDIR : Est un répertoire (tentative d’écriture dans un répertoire)
  • EMFILE : Trop de fichiers ouverts
  • EEXIST : Le fichier existe déjà (lors de l’utilisation des drapeaux ‘wx’ ou ‘ax’)

Cas d’utilisation pratiques

Création de fichiers de configuration

const fs = require('fs');

function createDefaultConfig() {
  const config = {
    apiKey: '',
    debug: false,
    logLevel: 'info',
    maxRetries: 3
  };
  
  try {
    fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
    console.log('Configuration par défaut créée');
  } catch (err) {
    console.error('Échec de création du fichier de configuration:', err);
  }
}

// Vérifier si la configuration existe, la créer sinon
try {
  fs.accessSync('config.json', fs.constants.F_OK);
  console.log('Le fichier de configuration existe déjà');
} catch (err) {
  createDefaultConfig();
}

Journalisation simple

function logMessage(message) {
  const timestamp = new Date().toISOString();
  const logEntry = `[${timestamp}] ${message}n`;
  
  try {
    fs.writeFileSync('app.log', logEntry, { flag: 'a' });
  } catch (err) {
    console.error('Échec d'écriture dans le fichier journal:', err);
  }
}

logMessage('Application démarrée');
// Faire quelque chose
logMessage('Opération terminée');

Sauvegarde des entrées utilisateur

const readline = require('readline-sync');
const fs = require('fs');

function saveUserData() {
  const username = readline.question('Entrez le nom d'utilisateur: ');
  
  if (!username) {
    console.log('Le nom d'utilisateur ne peut pas être vide');
    return saveUserData();
  }
  
  try {
    const userData = {
      username,
      createdAt: new Date().toISOString()
    };
    
    fs.writeFileSync(`${username}.json`, JSON.stringify(userData, null, 2));
    console.log(`Données utilisateur sauvegardées dans ${username}.json`);
  } catch (err) {
    console.error('Échec de sauvegarde des données utilisateur:', err);
  }
}

saveUserData();

Considérations de performance

Opérations synchrones vs. asynchrones

fs.writeFileSync() bloque la boucle d’événements jusqu’à ce que l’opération soit terminée, ce qui peut affecter les performances dans les applications à forte concurrence :

// Synchrone (bloque la boucle d'événements)
console.time('writeFileSync');
fs.writeFileSync('large-file.txt', 'X'.repeat(1000000));
console.timeEnd('writeFileSync');

// Asynchrone (ne bloque pas la boucle d'événements)
console.time('writeFile');
fs.writeFile('large-file-async.txt', 'X'.repeat(1000000), () => {
  console.timeEnd('writeFile');
});
console.log('Ceci s'exécute immédiatement pendant que le fichier est en cours d'écriture');

Quand utiliser fs.writeFileSync()

Utilisez fs.writeFileSync() quand :

  • Vous écrivez de petits fichiers
  • Vous travaillez dans un script ou un outil CLI
  • Le fichier doit être écrit avant de poursuivre l’exécution
  • Vous êtes dans une phase de démarrage/initialisation

Évitez fs.writeFileSync() quand :

  • Vous travaillez avec de gros fichiers
  • Dans des serveurs web à forte concurrence
  • Dans des chemins de code critiques pour les performances
  • Lorsque vous gérez plusieurs opérations de fichiers simultanément

Alternatives modernes

Utilisation de la syntaxe ES Modules

// Syntaxe ESM (nécessite Node.js 12+)
import { writeFileSync } from 'fs';
import { join } from 'path';

const filePath = join(process.cwd(), 'data.txt');
writeFileSync(filePath, 'Contenu utilisant la syntaxe ESM');

Utilisation de l’API fs/promises

Pour une approche plus moderne avec des promesses tout en conservant un comportement synchrone :

// Utilisation de fs/promises avec await au niveau supérieur (Node.js 14.8+ avec ESM)
import { writeFile } from 'fs/promises';

try {
  // C'est toujours asynchrone mais avec une syntaxe plus propre
  await writeFile('example.txt', 'Contenu avec promesses');
  console.log('Fichier écrit avec succès');
} catch (err) {
  console.error('Erreur d'écriture du fichier:', err);
}

Considérations de sécurité

Permissions de fichier

Soyez attentif aux permissions des fichiers lors de l’écriture de données sensibles :

// Définir des permissions restrictives pour les fichiers sensibles
fs.writeFileSync('credentials.json', JSON.stringify(credentials), {
  mode: 0o600  // Lecture/écriture pour le propriétaire uniquement
});

Vulnérabilités de traversée de chemin

Validez et assainissez toujours les chemins de fichiers, surtout lorsqu’ils proviennent d’entrées utilisateur :

const path = require('path');
const fs = require('fs');

function safeWriteFile(filename, content) {
  // Assainir le nom de fichier pour éviter la traversée de chemin
  const safeName = path.basename(filename);
  const safePath = path.join('./uploads', safeName);
  
  try {
    fs.writeFileSync(safePath, content);
    return true;
  } catch (err) {
    console.error('Erreur d'écriture du fichier:', err);
    return false;
  }
}

Résolution des problèmes courants

""ENOENT: no such file or directory""

Cela signifie généralement que le répertoire n’existe pas :

const fs = require('fs');
const path = require('path');

function writeFileWithDirectoryCreation(filePath, content) {
  const directory = path.dirname(filePath);
  
  try {
    // Créer le répertoire s'il n'existe pas
    if (!fs.existsSync(directory)) {
      fs.mkdirSync(directory, { recursive: true });
    }
    
    // Maintenant écrire le fichier
    fs.writeFileSync(filePath, content);
    return true;
  } catch (err) {
    console.error('Erreur:', err);
    return false;
  }
}

writeFileWithDirectoryCreation('logs/app/data.log', 'Entrée de journal');

""EACCES: permission denied""

Vérifiez les permissions des fichiers et répertoires :

try {
  fs.writeFileSync('/var/log/app.log', 'Entrée de journal');
} catch (err) {
  if (err.code === 'EACCES') {
    // Essayer d'écrire à un autre emplacement
    const homeDir = require('os').homedir();
    fs.writeFileSync(path.join(homeDir, 'app.log'), 'Entrée de journal');
    console.log('Écrit dans le répertoire personnel à la place');
  }
}

Conclusion

La méthode fs.writeFileSync() est un outil puissant pour gérer les opérations sur les fichiers dans les applications Node.js. Elle fournit un moyen simple d’écrire des données dans des fichiers de manière synchrone, ce qui la rend idéale pour les scénarios où vous avez besoin d’une garantie de finalisation avant de poursuivre l’exécution. En comprenant ses paramètres, la gestion des erreurs et les implications sur les performances, vous pouvez utiliser efficacement cette méthode dans vos projets tout en évitant les pièges courants.

N’oubliez pas de prendre en compte la nature synchrone de cette méthode et de l’utiliser de manière appropriée en fonction des besoins de votre application. Pour les applications à forte concurrence ou lorsque vous travaillez avec de gros fichiers, envisagez d’utiliser des alternatives asynchrones comme fs.writeFile() ou des flux.

FAQs

Utilisez fs.writeFileSync() lorsque vous avez besoin d'une garantie de finalisation avant de poursuivre l'exécution, généralement dans des scripts, des outils CLI ou du code d'initialisation. Utilisez fs.writeFile() pour la plupart des autres scénarios, en particulier dans les serveurs ou les applications où les performances sont importantes.

Utilisez le drapeau 'a' dans le paramètre options : fs.writeFileSync('log.txt', 'Nouvelle entréen', { flag: 'a' });

Non, il peut seulement créer des fichiers. Vous devez utiliser fs.mkdirSync() pour créer d'abord les répertoires.

Utilisez des objets Buffer : const buffer = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 'Hello' fs.writeFileSync('binary.dat', buffer);

Il n'y a pas de limite spécifique dans l'API, mais l'écriture de très gros fichiers de manière synchrone peut bloquer la boucle d'événements trop longtemps. Pour les fichiers de plus de quelques Mo, envisagez d'utiliser des flux ou des méthodes asynchrones.

Définissez l'option mode : fs.writeFileSync('sensitive.txt', 'données privées', { mode: 0o600 });

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers