Dans mon application Node, je dois supprimer un répertoire contenant des fichiers, mais fs.rmdir
ne fonctionne que sur des répertoires vides. Comment puis-je faire ceci?
Il existe un module pour cela appelé rimraf
( https://npmjs.org/package/rimraf ). Il fournit les mêmes fonctionnalités que rm -Rf
Async usage:
var rimraf = require("rimraf");
rimraf("/some/directory", function () { console.log("done"); });
Sync usage:
rimraf.sync("/some/directory");
Pour supprimer un dossier de manière synchrone
var fs = require('fs');
var deleteFolderRecursive = function(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(function(file, index){
var curPath = path + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
};
La plupart des personnes utilisant fs
avec Node.js aimeraient des fonctions proches de la "méthode Unix" de traitement des fichiers. J'utilise fs-extra pour apporter tout ce qui est cool:
fs-extra contient des méthodes qui ne sont pas incluses dans Vanilla Node.js package fs. Tels que mkdir -p, cp -r et rm -rf.
Mieux encore, fs-extra remplace avantageusement les fs natifs. Toutes les méthodes de fs ne sont pas modifiées et y sont attachées . Cela signifie que vous pouvez remplacer fs par fs-extra:
// this can be replaced
var fs = require('fs')
// by this
var fs = require('fs-extra')
Et puis vous pouvez supprimer un dossier de cette façon:
fs.removeSync('/tmp/myFolder');
//or
fs.remove('/tmp/myFolder', callback);
Ma réponse modifiée de @oconnecp ( https://stackoverflow.com/a/25069828/3027390 )
Utilise path.join pour une meilleure expérience multi-plateforme . Alors, n'oubliez pas de l'exiger.
var path = require('path');
Également renommé la fonction en rimraf
;)
/**
* Remove directory recursively
* @param {string} dir_path
* @see https://stackoverflow.com/a/42505874/3027390
*/
function rimraf(dir_path) {
if (fs.existsSync(dir_path)) {
fs.readdirSync(dir_path).forEach(function(entry) {
var entry_path = path.join(dir_path, entry);
if (fs.lstatSync(entry_path).isDirectory()) {
rimraf(entry_path);
} else {
fs.unlinkSync(entry_path);
}
});
fs.rmdirSync(dir_path);
}
}
Voici une version asynchrone de @ La réponse de SharpCoder
const fs = require('fs');
const path = require('path');
function deleteFile(dir, file) {
return new Promise(function (resolve, reject) {
var filePath = path.join(dir, file);
fs.lstat(filePath, function (err, stats) {
if (err) {
return reject(err);
}
if (stats.isDirectory()) {
resolve(deleteDirectory(filePath));
} else {
fs.unlink(filePath, function (err) {
if (err) {
return reject(err);
}
resolve();
});
}
});
});
};
function deleteDirectory(dir) {
return new Promise(function (resolve, reject) {
fs.access(dir, function (err) {
if (err) {
return reject(err);
}
fs.readdir(dir, function (err, files) {
if (err) {
return reject(err);
}
Promise.all(files.map(function (file) {
return deleteFile(dir, file);
})).then(function () {
fs.rmdir(dir, function (err) {
if (err) {
return reject(err);
}
resolve();
});
}).catch(reject);
});
});
});
};
J'ai écrit cette fonction appelée supprimer le dossier. Il supprimera récursivement tous les fichiers et dossiers d'un emplacement. Le seul package requis est async.
var async = require('async');
function removeFolder(location, next) {
fs.readdir(location, function (err, files) {
async.each(files, function (file, cb) {
file = location + '/' + file
fs.stat(file, function (err, stat) {
if (err) {
return cb(err);
}
if (stat.isDirectory()) {
removeFolder(file, cb);
} else {
fs.unlink(file, function (err) {
if (err) {
return cb(err);
}
return cb();
})
}
})
}, function (err) {
if (err) return next(err)
fs.rmdir(location, function (err) {
return next(err)
})
})
})
}
Si vous utilisez le noeud 8+ souhaitez une asyncronie et ne souhaitez pas de dépendances externes, voici la version async/wait:
const path = require('path');
const fs = require('fs');
const util = require('util');
const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);
const unlink = util.promisify(fs.unlink);
const rmdir = util.promisify(fs.rmdir);
const removeDir = async (dir) => {
try {
const files = await readdir(dir);
await Promise.all(files.map(async (file) => {
try {
const p = path.join(dir, file);
const stat = await lstat(p);
if (stat.isDirectory()) {
await removeDir(p);
} else {
await unlink(p);
console.log(`Removed file ${p}`);
}
} catch (err) {
console.error(err);
}
}))
await rmdir(dir);
console.log(`Removed dir ${dir}`);
} catch (err) {
console.error(err);
}
}
Je suis arrivé ici tout en essayant de surmonter la gulp
et j'écris pour d'autres.
gulp-clean
obsolète pour gulp-rimraf
gulp-rimraf
obsolète en faveur de delete-files-folders
Lorsque vous souhaitez supprimer des fichiers et des dossiers à l'aide de del
, vous devez ajouter /**
pour une suppression récursive.
gulp.task('clean', function () {
return del(['some/path/to/delete/**']);
});
En général, je ne ressuscite pas d'anciens threads, mais il y a un lot sur le désabonnement ici et sans que le rimraf réponde, tout cela me semble trop compliqué.
Tout d’abord dans le nœud moderne (> = v8.0.0), vous pouvez simplifier le processus en utilisant uniquement des modules de nœud, entièrement asynchrones, et paralléliser la suppression des liens de fichiers simultanément dans une fonction de cinq lignes tout en conservant la lisibilité:
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const rmdir = promisify(fs.rmdir);
const unlink = promisify(fs.unlink);
exports.rmdirs = async function rmdirs(dir) {
let entries = await readdir(dir, { withFileTypes: true });
return Promise.all(entries.map(entry => {
let fullPath = path.join(dir, entry.name);
return entry.isDirectory ? rmdir(fullPath) : unlink(fullPath);
}));
};
Sur une autre note, un garde pour attaques de traversée de chemin est inapproprié pour cette fonction car
rm -rf
dans la mesure où cela prend un argument et permettra à l'utilisateur de rm -rf /
s'il le lui demande. Il serait de la responsabilité d'un script de ne pas protéger le programme rm
lui-même..isDirectory
est false
pour les liens symboliques et sont non liés dans ce mode.Enfin et surtout, il existe une condition de concurrence rare que la récursion puisse générer une erreur si l'une des entrées était non liée ou supprimée outside ce script au bon moment au moment où cette récursion est en cours d'exécution. Étant donné que ce scénario n’est pas typique dans la plupart des environnements, il peut probablement être négligé. Toutefois, si nécessaire (pour certains cas Edge), ce problème peut être atténué avec cet exemple légèrement plus complexe:
exports.rmdirs = async function rmdirs(dir) {
let entries = await readdir(dir, { withFileTypes: true });
let results = Promise.all(entries.map(entry => {
let fullPath = path.join(dir, entry.name);
let task = entry.isDirectory ? rmdir(fullPath) : unlink(fullPath);
return task.catch(error => ({ error }));
}));
results.forEach(result => {
// Ignore missing files/directories; bail on other errors
if (result && result.error.code !== 'ENOENT') throw result.error;
});
};
La version async de @SharpCoder répond en utilisant fs.promises:
const afs = fs.promises;
const deleteFolderRecursive = async path => {
if (fs.existsSync(path)) {
for (let entry of await afs.readdir(path)) {
const curPath = path + "/" + entry;
if ((await afs.lstat(curPath)).isDirectory())
await deleteFolderRecursive(curPath);
else await afs.unlink(curPath);
}
await afs.rmdir(path);
}
};
Une méthode rapide et délicate (peut-être à des fins de test) pourrait consister à utiliser directement la méthode exec
ou spawn
pour appeler l'appel du système d'exploitation afin de supprimer le répertoire. En savoir plus sur NodeJs child_process .
let exec = require('child_process').exec
exec('rm -Rf /tmp/*.Zip', callback)
Les inconvénients sont:
Avantages:
dossier de synchronisation supprimer avec les fichiers ou seulement un fichier.
Je ne suis ni un donateur ni un contributeur, mais je ne pouvais pas trouver une bonne solution à ce problème et je devais trouver mon chemin ... alors j'espère que vous l'apprécierez :)
Fonctionne parfaitement pour moi avec un nombre quelconque de répertoires et de sous-répertoires imbriqués. Attention pour la portée de 'this' lors de la fonction récurrente, votre implémentation peut être différente. Dans mon cas, cette fonction reste dans le retour d'une autre fonction, c'est pourquoi je l'appelle avec ceci.
const fs = require('fs');
deleteFileOrDir(path, pathTemp = false){
if (fs.existsSync(path)) {
if (fs.lstatSync(path).isDirectory()) {
var files = fs.readdirSync(path);
if (!files.length) return fs.rmdirSync(path);
for (var file in files) {
var currentPath = path + "/" + files[file];
if (!fs.existsSync(currentPath)) continue;
if (fs.lstatSync(currentPath).isFile()) {
fs.unlinkSync(currentPath);
continue;
}
if (fs.lstatSync(currentPath).isDirectory() && !fs.readdirSync(currentPath).length) {
fs.rmdirSync(currentPath);
} else {
this.deleteFileOrDir(currentPath, path);
}
}
this.deleteFileOrDir(path);
} else {
fs.unlinkSync(path);
}
}
if (pathTemp) this.deleteFileOrDir(pathTemp);
}
Il suffit d’utiliser le module rmdir ! c'est facile et simple.
Version promisifiée:
const fs = require('fs')
const path = require('path')
const Q = require('q')
function rmdir(dir) {
return Q.nfcall(fs.access, dir).then(() => {
return Q.nfcall(fs.readdir, dir)
.then(files => files.reduce((pre, f) => pre.then(() => {
var sub = path.join(dir, f)
return Q.nfcall(fs.lstat, sub).then(stat => {
if (stat.isDirectory()) return rmdir(sub)
return Q.nfcall(fs.unlink, sub)
})
}), Q()))
}, err => {})
.then(() => Q.nfcall(fs.rmdir, dir))
}
J'aimerais qu'il y ait un moyen de faire cela sans modules supplémentaires pour quelque chose d'aussi minuscule et commun, mais c'est le meilleur que je pourrais trouver.
Mise à jour: Devrait maintenant fonctionner sous Windows (testé Windows 10) et devrait également fonctionner sur les systèmes Linux/Unix/BSD/Mac.
const
execSync = require("child_process").execSync,
fs = require("fs"),
os = require("os");
let removeDirCmd, theDir;
removeDirCmd = os.platform() === 'win32' ? "rmdir /s /q " : "rm -rf ";
theDir = __dirname + "/../web-ui/css/";
// WARNING: Do not specify a single file as the windows rmdir command will error.
if (fs.existsSync(theDir)) {
console.log(' removing the ' + theDir + ' directory.');
execSync(removeDirCmd + '"' + theDir + '"', function (err) {
console.log(err);
});
}
// sans utiliser de bibliothèque tierce
const fs = require('fs');
var FOLDER_PATH = "./dirname";
var files = fs.readdirSync(FOLDER_PATH);
files.forEach(element => {
fs.unlinkSync(FOLDER_PATH + "/" + element);
});
fs.rmdirSync(FOLDER_PATH);
Ultra-rapide et infaillible
Vous pouvez utiliser le package lignator
( --- (https://www.npmjs.com/package/lignator ), il est plus rapide que tout code asynchrone (par exemple, rimraf) et plus infaillible (en particulier sous Windows, où la suppression de fichier n’est pas instantanée et les fichiers peuvent être verrouillés par d’autres processus).
4,36 Go de données, 28 042 fichiers, 4 217 dossiers sous Windows supprimés en 15 secondes par rapport aux 60 secondes de rimraf le ancien disque dur.
const lignator = require('lignator');
lignator.remove('./build/');
const fs = require('fs')
const path = require('path')
let _dirloc = '<path_do_the_directory>'
if(existsSync(_dirloc))
{
fs.readdir(path, (err, files) => {
if(!err) {
for(let file of files) {
// Delete each file
fs.unlinkSync(path.join(_dirloc,file))
}
}
})
// After the done of each file delete,
// Delete the directory itself
if(fs.unlinkSync(_dirloc)) {
console.log('Directory has been deleted!')
}
}
Une autre alternative consiste à utiliser le module fs-promise
qui fournit des versions promises du module fs-extra
modules.
vous pourriez alors écrire comme ceci:
const { remove, mkdirp, writeFile, readFile } = require('fs-promise')
const { join, dirname } = require('path')
async function createAndRemove() {
const content = 'Hello World!'
const root = join(__dirname, 'foo')
const file = join(root, 'bar', 'baz', 'hello.txt')
await mkdirp(dirname(file))
await writeFile(file, content)
console.log(await readFile(file, 'utf-8'))
await remove(join(__dirname, 'foo'))
}
createAndRemove().catch(console.error)
remarque: async/wait requiert une version récente de nodejs (7.6+)
C'est une approche utilisant promisify et deux fonctions d'aide (to et toAll) pour résoudre la promesse.
Il fait toutes les actions de manière asynchrone.
const fs = require('fs');
const { promisify } = require('util');
const to = require('./to');
const toAll = require('./toAll');
const readDirAsync = promisify(fs.readdir);
const rmDirAsync = promisify(fs.rmdir);
const unlinkAsync = promisify(fs.unlink);
/**
* @author Aécio Levy
* @function removeDirWithFiles
* @usage: remove dir with files
* @param {String} path
*/
const removeDirWithFiles = async path => {
try {
const file = readDirAsync(path);
const [error, files] = await to(file);
if (error) {
throw new Error(error)
}
const arrayUnlink = files.map((fileName) => {
return unlinkAsync(`${path}/${fileName}`);
});
const [errorUnlink, filesUnlink] = await toAll(arrayUnlink);
if (errorUnlink) {
throw new Error(errorUnlink);
}
const deleteDir = rmDirAsync(path);
const [errorDelete, result] = await to(deleteDir);
if (errorDelete) {
throw new Error(errorDelete);
}
} catch (err) {
console.log(err)
}
};