JavaScript est depuis longtemps l’un des langages de script les plus populaires, mais pendant une longue période, il n’était pas un excellent choix pour le développement d’applications backend côté serveur. Puis est apparu Node.js, qui permet de créer des applications légères côté serveur, pilotées par les événements et construites à l’aide du langage de programmation JavaScript.

Node.js est un moteur d’exécution JavaScript à code source ouvert qui peut être téléchargé et installé gratuitement sur tous les principaux systèmes d’exploitation (Windows, Mac, Linux). Il est devenu de plus en plus populaire auprès des créateurs d’applications ces dernières années, et il a fourni de nombreuses nouvelles opportunités d’emploi pour les développeurs JavaScript à la recherche d’une spécialité.

Dans cet article, nous allons apprendre à gérer le système de fichiers à l’aide de Node.js. Il est facile d’utiliser les API de Node.js pour interagir avec le système de fichiers et effectuer de nombreuses opérations complexes, et savoir comment les manœuvrer stimulera votre productivité.

Plongeons !

Pré-requis pour la compréhension du système de fichiers de Node.js

Le premier pré-requis est l’installation de Node.js sur votre système d’exploitation. Node.js ne nécessite aucun matériel complexe pour fonctionner, ce qui rend facile le téléchargement et l’installation de Node.js sur votre ordinateur.

Il serait utile que vous ayez également une connaissance de base de JavaScript pour travailler sur des modules Node.js comme les systèmes de fichiers (également connus sous le nom de « File System ou FS »). Une compréhension de haut niveau des fonctions JavaScript, des fonctions de rappel et des promesses vous aidera à maîtriser ce sujet encore plus rapidement.

Module de système de fichiers de Node.js

Travailler avec des fichiers et des répertoires est l’un des besoins fondamentaux d’une application full-stack. Vos utilisateurs peuvent vouloir téléverser des images, des CV ou d’autres fichiers vers un serveur. En même temps, votre application peut avoir besoin de lire des fichiers de configuration, de déplacer des fichiers, ou même de changer leurs permissions de manière programmatique.

Le module de système de fichiers de Node.js a tout prévu. Il fournit plusieurs API pour interagir avec les systèmes de fichiers de manière transparente. La plupart des API sont personnalisables avec des options et des drapeaux. Vous pouvez également les utiliser pour effectuer des opérations de fichiers synchrones et asynchrones.

Avant de nous plonger dans le module de système de fichiers, jetons un coup d’œil à ce qu’est le module Node.js.

Modules Node.js

Les modules Node.js sont un ensemble de fonctionnalités disponibles sous forme d’API qu’un programme consommateur peut utiliser. Par exemple, vous avez le module fs pour interagir avec le système de fichiers. De même, un module http utilise ses fonctions pour créer un serveur et bien d’autres opérations. Node.js offre de nombreux modules pour abstraire de nombreuses fonctionnalités de bas niveau pour vous.

Vous pouvez également créer vos propres modules. À partir de la version 14 de Node.js, vous pouvez créer et utiliser des modules Node.js de deux manières : Les modules CommonJS (CJS) et ESM (MJS). Tous les exemples que nous verrons dans cet article sont dans le style CJS.

Travailler avec des fichiers dans Node.js

Travailler avec des fichiers implique diverses opérations avec des fichiers et des répertoires (dossiers). Nous allons maintenant apprendre chacune de ces opérations à l’aide d’un exemple de code source. Veuillez ouvrir votre éditeur de code source préféré et essayez-les au fur et à mesure de votre lecture.

Tout d’abord, importez le module fs dans votre fichier source pour commencer à travailler avec les méthodes du système de fichiers. Dans le style CJS, nous utilisons la méthode require pour importer une méthode d’un module. Ainsi, pour importer et utiliser les méthodes du module fs, vous devez procéder comme ceci :

const { writeFile } = require('fs/promises');

Notez également que nous importons la méthode writeFile du module fs/promises. Nous voulons utiliser les méthodes promises car elles sont les plus récentes, et elles sont faciles à utiliser avec les mots-clés async/await et moins de code. Les autres alternatives sont les méthodes synchrones et les fonctions de rappel que nous verrons plus tard.

Comment créer et écrire dans un fichier

Vous pouvez créer et écrire dans un fichier de trois façons :

  1. Utilisation de la méthode writeFile
  2. Utilisation de la méthode appendFile
  3. Utilisation de la méthode openFile

Ces méthodes acceptent un chemin de fichier et les données comme contenu à écrire dans le fichier. Si le fichier existe, elles remplacent le contenu dans le fichier. Sinon, elles créent un nouveau fichier avec le contenu.

1. Utilisation de la méthode writeFile

L’extrait de code ci-dessous montre l’utilisation de la méthode writeFile. Commencez par créer un fichier appelé createFile.js à l’aide de l’extrait de code ci-dessous :

const { writeFile } = require('fs/promises');
async function writeToFile(fileName, data) {
  try {
    await writeFile(fileName, data);
    console.log(`Wrote data to ${fileName}`);
  } catch (error) {
    console.error(`Got an error trying to write the file: ${error.message}`);
  }
}

Notez que nous utilisons le mot-clé await pour invoquer la méthode car elle renvoie une promesse JavaScript. Une promesse réussie créera/écrira dans le fichier. Nous avons un bloc try-catch pour gérer les erreurs en cas de rejet de la promesse.

Nous pouvons maintenant invoquer la fonction writeToFile:

writeToFile('friends.txt', 'Bob');

Ensuite, ouvrez une invite de commande ou un terminal et exécutez le programme ci-dessus en utilisant la commande suivante :

node createFile.js

Cela créera un nouveau fichier appelé friends.txt avec une ligne qui dit simplement :

Bob

2. Utilisation de la méthode appendFile

Comme son nom l’indique, l’utilisation principale de cette méthode est d’ajouter et de modifier un fichier. Toutefois, vous pouvez également utiliser la même méthode pour créer un fichier.

Jetez un coup d’œil à la fonction ci-dessous. Nous utilisons la méthode appendFile avec l’indicateur w pour écrire un fichier. L’indicateur par défaut pour l’ajout à un fichier est a:

const { appendFile} = require('fs/promises');

async function appendToFile(fileName, data) {
  try {
    await appendFile(fileName, data, { flag: 'w' });
    console.log(`Appended data to ${fileName}`);
  } catch (error) {
    console.error(`Got an error trying to append the file: {error.message}`);
  }
}

Maintenant, vous pouvez invoquer la fonction ci-dessus comme ceci :

appendToFile('activities.txt', 'Skiing');

Ensuite, vous pouvez exécuter le code dans l’environnement Node.js en utilisant la commande node, comme nous l’avons vu précédemment. Cela créera un fichier appelé activities.txt avec le contenu Skiing dedans.

3. Utilisation de la méthode open

La dernière méthode que nous allons apprendre pour créer et écrire dans un fichier est la méthode open. Vous pouvez ouvrir un fichier en utilisant l’indicateur w (pour « write ») :

const { open} = require('fs/promises');

async function openFile(fileName, data) {
  try {
    const file = await open(fileName, 'w');
    await file.write(data);
    console.log(`Opened file ${fileName}`);
  } catch (error) {
    console.error(`Got an error trying to open the file: {error.message}`);
  }
}

Appelez maintenant la fonction openFile avec :

openFile('tasks.txt', 'Do homework');

Lorsque vous exécutez le script à l’aide de la commande node, vous aurez un fichier nommé tasks.txt créé avec le contenu initial :

Do homework

Comment lire un fichier

Maintenant que nous savons comment créer et écrire dans un fichier, apprenons à lire le contenu d’un fichier. Pour ce faire, nous utiliserons la méthode readFile du module de système de fichiers.

Créez un fichier appelé readThisFile.js avec le code suivant :

// readThisFile.js
const { readFile } = require('fs/promises');
async function readThisFile(filePath) {
  try {
    const data = await readFile(filePath);
    console.log(data.toString());
  } catch (error) {
    console.error(`Got an error trying to read the file: {error.message}`);
 }
}

Lisons maintenant les trois fichiers que nous avons créés en invoquant la fonction readThisFile:

readThisFile('activities.txt');
readThisFile('friends.txt');
readThisFile('tasks.txt');

Enfin, exécutez le script à l’aide de la commande node suivante :

node readThisFile.js

Vous devriez voir la sortie suivante dans la console :

Skiing
Do homework
Bob

Un point à noter ici : La méthode readFile lit le fichier de manière asynchrone. Cela signifie que l’ordre dans lequel vous lisez le fichier et l’ordre dans lequel vous obtenez une réponse à afficher dans la console peuvent ne pas être les mêmes. Vous devez utiliser la version synchrone de la méthode readFile pour obtenir l’ordre. Nous verrons cela ici dans un petit moment.

Comment renommer un fichier

Pour renommer un fichier, utilisez la méthode rename du module fs. Créons un fichier appelé rename-me.txt. Nous renommerons ce fichier de manière programmatique.

Créez un fichier appelé renameFile.js avec le code suivant :

const { rename } = require('fs/promises');

async function renameFile(from, to) {
  try {
    await rename(from, to);
    console.log(`Renamed ${from} to ${to}`);
  } catch (error) {
    console.error(`Got an error trying to rename the file: ${error.message}`);
  }
}

Comme vous l’avez peut-être remarqué, la méthode rename prend deux arguments. L’un est le fichier avec le nom source, et l’autre est le nom cible.

Invoquons maintenant la fonction ci-dessus pour renommer le fichier :

const oldName = "rename-me.txt";
const newName = "renamed.txt";
renameFile(oldName, newName);

Comme précédemment, exécutez le fichier script en utilisant la commande node pour renommer le fichier :

node renameFile.js

Comment déplacer un fichier

Déplacer un fichier d’un répertoire à un autre est similaire au fait de renommer son chemin. Nous pouvons donc utiliser la méthode rename elle-même pour déplacer des fichiers.

Créons deux dossiers, from et to. Puis nous créerons un fichier appelé move-me.txt à l’intérieur du dossier from.

Ensuite, nous allons écrire le code pour déplacer le fichier move-me.txt. Créez un fichier appelé moveFile.js avec l’extrait de code suivant :

const { rename } = require('fs/promises');
const { join } = require('path');
async function moveFile(from, to) {
  try {
    await rename(from, to);
    console.log(`Moved ${from} to ${to}`);
  } catch (error) {
    console.error(`Got an error trying to move the file: ${error.message}`);
  }
}

Comme vous pouvez le voir, nous utilisons la méthode rename comme auparavant. Mais pourquoi devons-nous importer la méthode join du module path (oui, le chemin est un autre module crucial de Node.js) ?

La méthode join est utilisée pour joindre plusieurs segments de chemin spécifiés pour former un chemin. Nous allons l’utiliser pour former le chemin des noms de fichiers source et destination :

const fromPath = join(__dirname, "from", "move-me.txt");
const destPath = join(__dirname, "to", "move-me.txt");
moveFile(fromPath, destPath);

Et c’est tout ! Si vous exécutez le script moveFile.js, vous verrez le fichier move-me.txt déplacé vers le dossier to.

Comment copier un fichier

Nous utilisons la méthode copyFile du module fs pour copier un fichier de la source vers la destination.

Jetez un coup d’œil à l’extrait de code ci-dessous :

const { copyFile } = require('fs/promises');
const { join } = require('path');
async function copyAFile(from, to) {
  try {
    await copyFile(from, to);
    console.log(`Copied ${from} to ${to}`);
  } catch (err) {
    console.error(`Got an error trying to copy the file: ${err.message}`);
  }
}

Vous pouvez maintenant invoquer la fonction ci-dessus avec :

copyAFile('friends.txt', 'friends-copy.txt');

Elle copiera le contenu du fichier friends.txt dans le fichier friends-copy.txt.

Maintenant, c’est génial, mais comment copier plusieurs fichiers ?

Vous pouvez utiliser l’API Promise.all pour exécuter plusieurs promesses en parallèle :

async function copyAll(fromDir, toDir, filePaths) {
  return Promise.all(filePaths.map(filePath => {
   return copyAFile(join(fromDir, filePath), join(toDir, filePath));
  }));
}

Vous pouvez maintenant fournir tous les chemins de fichiers à copier d’un répertoire à un autre :

copyFiles('from', 'to', ['copyA.txt', 'copyB.txt']);

Vous pouvez également utiliser cette approche pour effectuer d’autres opérations comme le déplacement, l’écriture et la lecture de fichiers en parallèle.

Comment supprimer un fichier

Nous utilisons la méthode unlink pour supprimer un fichier :

const { unlink } = require('fs/promises');
async function deleteFile(filePath) {
  try {
    await unlink(filePath);
    console.log(`Deleted ${filePath}`);
  } catch (error) {
    console.error(`Got an error trying to delete the file: ${error.message}`);
  }
}

N’oubliez pas que vous devez fournir le chemin d’accès au fichier pour le supprimer :

deleteFile('delete-me.txt');

Notez que si le chemin d’accès est un lien symbolique vers un autre fichier, la méthode unlink annulera le lien symbolique, mais le fichier original ne sera pas touché. Nous parlerons davantage des liens symboliques plus tard.

Comment modifier les permissions et la propriété des fichiers

Il se peut que vous souhaitiez à un moment donné modifier les autorisations de fichiers de manière programmatique. Cela peut s’avérer très utile pour rendre un fichier en lecture seule ou entièrement accessible.

Nous allons utiliser la méthode chmod pour modifier l’autorisation d’un fichier :

const { chmod } = require('fs/promises');
async function changePermission(filePath, permission) {
  try {
    await chmod(filePath, permission);
    console.log(`Changed permission to ${permission} for ${filePath}`);
  } catch (error) {
    console.error(`Got an error trying to change permission: ${error.message}`);
  }
}

Nous pouvons passer le chemin du fichier et le bitmask de permission pour changer la permission.

Voici l’appel de fonction pour changer la permission d’un fichier en lecture seule :

changePermission('permission.txt', 0o400);

De façon similaire à la permission, vous pouvez également changer la propriété d’un fichier de façon programmatique. Nous utilisons la méthode chown pour ce faire :

const { chown } = require('fs/promises');

async function changeOwnership(filePath, userId, groupId) {
  try {
    await chown(filePath, userId, groupId);
    console.log(`Changed ownership to ${userId}:${groupId} for ${filePath}`);
  } catch (error) {
    console.error(`Got an error trying to change ownership: ${error.message}`);
  }
}

Nous appelons ensuite la fonction avec le chemin du fichier, l’ID de l’utilisateur et l’ID du groupe :

changeOwnership('ownership.txt', 1000, 1010);

Comment créer un lien symbolique

Le lien symbolique (également appelé symlink) est un concept du système de fichiers permettant de créer un lien vers un fichier ou un dossier. Nous créons des liens symboliques pour créer des raccourcis vers un fichier/dossier cible dans le système de fichiers. Le module Node.js filesystem fournit la méthode symlink pour créer un lien symbolique.

Pour créer le lien symbolique, nous devons passer le chemin du fichier cible, le chemin du fichier réel et le type :

const { symlink } = require('fs/promises');
const { join } = require('path');
async function createSymlink(target, path, type) {
  try {
    await symlink(target, path, type);
    console.log(`Created symlink to ${target} at ${path}`);
  } catch (error) {
    console.error(`Got an error trying to create the symlink: ${error.message}`);
  }
}

Nous pouvons invoquer la fonction avec :

createSymlink('join(__dirname, from, symMe.txt)', 'symToFile', 'file');

Ici, nous avons créé un lien symbolique appelé symToFile.

Comment observer les modifications d’un fichier

Saviez-vous que vous pouvez observer les modifications apportées à un fichier ? C’est un excellent moyen de surveiller les altérations et les événements, surtout lorsque vous ne vous y attendez pas. Vous pouvez les capturer et les auditer pour les examiner ultérieurement.

La méthode watch est la meilleure façon de surveiller les modifications de fichiers. Il existe une autre méthode appelée watchFile, mais elle n’est pas aussi performante que la méthode watch.

Jusqu’à présent, nous avons utilisé la méthode du module de système de fichiers avec les mots-clés async/await. Voyons les utilisations de la fonction callback avec cet exemple.

La méthode watch accepte le chemin du fichier et une fonction de rappel comme arguments. Chaque fois qu’une activité a lieu sur le fichier, la fonction callback est appelée.

Nous pouvons tirer parti du paramètre event pour obtenir plus d’informations sur les activités :

const fs = require('fs');
function watchAFile(file) {
  fs.watch(file, (event, filename) => {
    console.log(`${filename} file Changed`);
  });
}

Appelez la fonction en passant un nom de fichier à watch:

watchAFile('friends.txt');

Maintenant, nous capturons automatiquement toute activité sur le fichier friends.txt.

Travailler avec des répertoires (dossiers) dans Node.js

Passons maintenant à l’apprentissage de la manière d’effectuer des opérations sur des répertoires ou des dossiers. La plupart des opérations telles que renommer, déplacer et copier sont similaires à ce que nous avons vu pour les fichiers. Cependant, certaines méthodes et opérations spécifiques ne sont utilisables que sur les répertoires.

Comment créer un répertoire

Nous utilisons la méthode mkdir pour créer un répertoire. Vous devez passer le nom du répertoire comme argument :

const { mkdir } = require('fs/promises');
async function createDirectory(path) {
  try {
    await mkdir(path);
    console.log(`Created directory ${path}`);
  } catch (error) {
    console.error(`Got an error trying to create the directory: ${error.message}`);
  }
}

Maintenant, nous pouvons invoquer la fonction createDirectory avec un chemin de répertoire :

createDirectory('new-directory');

Cela créera un répertoire nommé new-directory.

Comment créer un répertoire temporaire

Les répertoires temporaires ne sont pas des répertoires ordinaires. Ils ont une signification particulière pour le système d’exploitation. Vous pouvez créer un répertoire temporaire à l’aide de la méthode mkdtemp().

Créons un dossier temporaire dans le répertoire temporaire de votre système d’exploitation. Nous obtenons les informations relatives à l’emplacement du répertoire temporaire à partir de la méthode tmpdir() du module os:

const { mkdtemp } = require('fs/promises');
const { join } = require('path');
const { tmpdir } = require('os');
async function createTemporaryDirectory(fileName) {
  try {
    const tempDirectory = await mkdtemp(join(tmpdir(), fileName));
    console.log(`Created temporary directory ${tempDirectory}`);
  } catch (error) {
    console.error(`Got an error trying to create the temporary directory: ${error.message}`);
  }
}

Maintenant, appelons la fonction avec le nom du répertoire pour le créer :

createTemporaryDirectory('node-temp-file-');

Notez que Node.js ajoutera six caractères aléatoires à la fin du nom du répertoire temporaire créé pour le garder unique.

Comment supprimer un répertoire

Vous devez utiliser la méthode rmdir() pour supprimer/effacer un répertoire :

const { rmdir } = require('fs/promises');
async function deleteDirectory(path) {
  try {
    await rmdir(path);
    console.log(`Deleted directory ${path}`);
  } catch (error) {
    console.error(`Got an error trying to delete the directory: ${error.message}`);
  }
}

Ensuite, appelez la fonction ci-dessus en passant le chemin du dossier que vous voulez supprimer :

deleteDirectory('new-directory-renamed');

API synchrones vs asynchrones

Jusqu’à présent, nous avons vu de nombreux exemples de méthodes de système de fichiers, et tous sont avec des utilisations asynchrones. Cependant, vous pouvez avoir besoin de traiter certaines opérations de manière synchrone.

Un exemple d’opération synchrone est la lecture de plusieurs fichiers l’un après l’autre. Le module fs possède une méthode appelée readFileSync() pour y parvenir :

const { readFileSync } = require('fs');
function readFileSynchronously(path) {
  try {
    const data = readFileSync(path);
    console.log(data.toString());
  } catch (error) {
    console.error(error);
  }
}

Notez que la méthode readFileSync() n’est pas requise du paquet « fs/promesses ». Ceci est dû au fait que la méthode n’est pas asynchrone. Ainsi, vous pouvez appeler la fonction ci-dessus avec :

readFileSynchronously('activities.txt');
readFileSynchronously('friends.txt');
readFileSynchronously('tasks.txt');

Dans ce cas, tous les fichiers ci-dessus seront lus dans l’ordre où les fonctions ont été appelées.

Le module de système de fichiers Node.js offre une méthode synchrone pour d’autres opérations comme l’opération de lecture. Utilisez-les à bon escient et uniquement en cas de besoin. Les méthodes asynchrones sont bien plus utiles pour l’exécution parallèle.

Gestion des erreurs

Comme tout codeur le sait, vous devez vous attendre à des erreurs et être prêt à les gérer lorsque vous effectuez une opération sur un fichier ou un répertoire. Que se passe-t-il si le fichier n’est pas trouvé, ou si vous n’avez pas la permission d’écrire dans un fichier ? Il peut y avoir (et il y aura probablement) de nombreux cas où vous pourriez rencontrer une erreur.

Vous devriez toujours entourer vos appels de méthode d’un bloc try-catch. De cette façon, si une erreur se produit, le contrôle passera au bloc catch, où vous pourrez examiner et traiter l’erreur. Comme vous l’avez peut-être remarqué dans tous les exemples ci-dessus, nous avons utilisé le bloc try-catch pour traiter les erreurs que nous avons rencontrées.

Résumé

Passons en revue les points clés que nous avons abordés dans ce tutoriel :

  • Le module de système de fichiers (fs) de Node.js dispose de nombreuses méthodes pour vous aider dans de nombreuses tâches de bas niveau.
  • Vous pouvez effectuer diverses opérations sur les fichiers comme créer, écrire, renommer, copier, déplacer, supprimer, et bien d’autres encore.
  • Vous pouvez effectuer plusieurs opérations sur les répertoires comme la création, le répertoire temporaire, le déplacement, et bien d’autres encore.
  • Toutes les méthodes peuvent être invoquées de manière asynchrone en utilisant des promesses JavaScript ou des fonctions de rappel.
  • Vous pouvez également invoquer les méthodes de manière synchrone si nécessaire.
  • Préférez toujours les méthodes asynchrones aux méthodes synchrones.
  • Gérez les erreurs avec un bloc try-catch à chaque fois que vous interagissez avec les méthodes.

Maintenant que nous avons travaillé un peu avec le système de fichiers Node.js, vous devriez avoir une bonne maîtrise de ses tenants et aboutissants. Si vous voulez renforcer votre savoir-faire, vous pouvez vous pencher sur les flux Node.js comme une progression naturelle de l’apprentissage des modules Node.js. Les flux sont des moyens efficaces de gérer l’échange d’informations, y compris les appels réseau, la lecture/écriture de fichiers, et bien plus encore.

Vous pouvez trouver tout le code source utilisé dans cet article dans ce dépôt GitHub.

Vous prévoyez d’utiliser Node.js pour votre prochain projet ? Faites-nous savoir pourquoi vous l’avez choisi dans la section des commentaires ci-dessous.