Copie de fichier
NodeJS fournit une API de base pour les opérations sur les fichiers, mais les fonctions avancées telles que la copie de fichiers ne sont pas fournies, alors pratiquons d'abord avec le programme de copie de fichiers. Semblable à la commande copy, notre programme doit pouvoir accepter deux paramètres : le chemin du fichier source et le chemin du fichier cible.
Copie de petit fichier
Nous utilisons le module fs intégré de NodeJS pour implémenter simplement ce programme comme suit.
var fs = require('fs'); function copy(src, dst) { fs.writeFileSync(dst, fs.readFileSync(src)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2));
Le programme ci-dessus utilise fs.readFileSync pour lire le contenu du fichier à partir du chemin source et utilise fs.writeFileSync pour écrire le contenu du fichier dans le chemin cible.
Connaissance du Bean : le processus est une variable globale et les paramètres de ligne de commande peuvent être obtenus via process.argv. Puisque argv[0] est fixement égal au chemin absolu du programme exécutable NodeJS et que argv[1] est fixement égal au chemin absolu du module principal, le premier paramètre de ligne de commande commence à partir de la position argv[2].
Copie de fichier volumineux
Le programme ci-dessus n'a aucun problème à copier certains petits fichiers, mais cette méthode consistant à lire tout le contenu du fichier dans la mémoire en même temps, puis à l'écrire immédiatement sur le disque n'est pas adaptée à la copie de fichiers volumineux, et la mémoire être épuisé. Pour les gros fichiers, on ne peut lire qu'un peu et écrire un peu jusqu'à ce que la copie soit terminée. Par conséquent, le programme ci-dessus doit être modifié comme suit.
var fs = require('fs'); function copy(src, dst) { fs.createReadStream(src).pipe(fs.createWriteStream(dst)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2));
Le programme ci-dessus utilise fs.createReadStream pour créer un flux de données en lecture seule pour le fichier source, et utilise fs.createWriteStream pour créer un flux de données en écriture seule pour le fichier cible, et utilise la méthode pipe pour connecter les deux. flux de données. Ce qui se passe une fois le raccordement effectué, pour le dire de manière plus abstraite, c'est que l'eau s'écoule d'un seau à l'autre le long du tuyau.
Répertoires de parcours
Parcourir des répertoires est une exigence courante lors de la manipulation de fichiers. Par exemple, lors de l'écriture d'un programme qui doit rechercher et traiter tous les fichiers JS dans un répertoire spécifié, il doit parcourir l'intégralité du répertoire.
Algorithme récursif
Les algorithmes récursifs sont généralement utilisés lors de la traversée de répertoires, sinon il sera difficile d'écrire du code concis. Les algorithmes récursifs sont similaires à l’induction mathématique dans la mesure où ils résolvent des problèmes en réduisant continuellement leur taille. L’exemple suivant illustre cette approche.
function factorial(n) { if (n === 1) { return 1; } else { return n * factorial(n - 1); } }
La fonction ci-dessus est utilisée pour calculer la factorielle de N (N !). Comme vous pouvez le constater, lorsque N est supérieur à 1, le problème se réduit au calcul de la factorielle de N fois N-1. Lorsque N est égal à 1, le problème atteint sa taille minimale et aucune simplification supplémentaire n'est nécessaire, donc 1 est renvoyé directement.
Piège : bien que le code écrit à l'aide de l'algorithme récursif soit simple, puisque chaque récursion génère un appel de fonction, lorsque les performances doivent être priorisées, l'algorithme récursif doit être converti en algorithme de boucle pour réduire le nombre d'appels de fonction.
Algorithme de traversée
Le répertoire est une structure arborescente, et l'algorithme de parcours en profondeur d'abord + pré-ordre est généralement utilisé lors du parcours. La profondeur signifie d'abord qu'après avoir atteint un nœud, traversez d'abord les nœuds enfants au lieu des nœuds voisins. Le parcours de précommande signifie que le parcours est terminé lorsqu'un nœud est atteint pour la première fois, plutôt que la dernière fois qu'il revient à un nœud. Par conséquent, lorsque vous utilisez cette méthode de parcours, l'ordre de parcours de l'arbre ci-dessous est A >
A / \ B C / \ \ D E F
Traversée synchrone
Après avoir compris les algorithmes nécessaires, nous pouvons simplement implémenter la fonction de traversée de répertoire suivante.
function travel(dir, callback) { fs.readdirSync(dir).forEach(function (file) { var pathname = path.join(dir, file); if (fs.statSync(pathname).isDirectory()) { travel(pathname, callback); } else { callback(pathname); } }); }
Comme vous pouvez le voir, cette fonction utilise un répertoire comme point de départ du parcours. Lorsqu'un sous-répertoire est rencontré, le sous-répertoire est parcouru en premier. Lorsqu'un fichier est rencontré, le chemin absolu du fichier est transmis à la fonction de rappel. Une fois que la fonction de rappel a obtenu le chemin du fichier, elle peut effectuer divers jugements et processus. Disons donc que nous avons le répertoire suivant :
- /home/user/ - foo/ x.js - bar/ y.js z.css
Lorsque vous parcourez le répertoire à l'aide du code suivant, l'entrée obtenue est la suivante.
travel('/home/user', function (pathname) { console.log(pathname); });
/home/user/foo/x.js /home/user/bar/y.js /home/user/z.css
Parcours asynchrone
Si l'API asynchrone est utilisée lors de la lecture du répertoire ou de l'état du fichier, la fonction de parcours de répertoire sera un peu compliquée à mettre en œuvre, mais le principe est exactement le même. La version asynchrone de la fonction de déplacement est la suivante.
function travel(dir, callback, finish) { fs.readdir(dir, function (err, files) { (function next(i) { if (i < files.length) { var pathname = path.join(dir, files[i]); fs.stat(pathname, function (err, stats) { if (stats.isDirectory()) { travel(pathname, callback, function () { next(i + 1); }); } else { callback(pathname, function () { next(i + 1); }); } }); } else { finish && finish(); } }(0)); }); }
Les compétences d'écriture des fonctions de parcours asynchrones ne seront pas présentées en détail ici. Elles seront présentées en détail dans les chapitres suivants. Bref, on voit que la programmation asynchrone est assez compliquée.