Comment créer un chemin complet avec le fs.mkdirSync du nœud?


159

J'essaye de créer un chemin complet s'il n'existe pas.

Le code ressemble à ceci:

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest); 

Ce code fonctionne très bien tant qu'il n'y a qu'un seul sous-répertoire (un newDest comme 'dir1') mais quand il y a un chemin de répertoire comme ('dir1 / dir2'), il échoue avec l' erreur: ENOENT, pas de tel fichier ou répertoire

J'aimerais pouvoir créer le chemin complet avec aussi peu de lignes de code que nécessaire.

J'ai lu qu'il y avait une option récursive sur fs et l'ai essayé comme ça

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest,'0777', true);

Je pense que cela devrait être aussi simple que de créer récursivement un répertoire qui n'existe pas. Est-ce que je manque quelque chose ou dois-je analyser le chemin et vérifier chaque répertoire et le créer s'il n'existe pas déjà?

Je suis assez nouveau sur Node. J'utilise peut-être une ancienne version de FS?


1
github.com/substack/node-mkdirp et toutes sortes d'autres solutions sur cette recherche Google .
jfriend00

4
@AndyRay Cette question StackOverflow est maintenant le meilleur résultat de google pour cette question, ce qui est drôle parce que cela signifie qu'elle est récurrente ....
Matt Parkins

1
C'était un problème sur les anciennes versions de Node, la mise à jour vers Node 12+ résout le problème
MrJomp

Réponses:


48

Une option consiste à utiliser le module shelljs

npm installer shelljs

var shell = require('shelljs');
shell.mkdir('-p', fullPath);

À partir de cette page:

Options disponibles:

p: chemin complet (créera des répertoires intermédiaires si nécessaire)

Comme d'autres l'ont noté, il existe d'autres modules plus ciblés. Mais, en dehors de mkdirp, il a des tonnes d'autres opérations shell utiles (comme which, grep etc ...) et il fonctionne sur windows et * nix


2
Merci! J'ai fini par utiliser exec (j'utilisais déjà ceci) et cela a fonctionné comme un charme. var exec = require ('processus_enfant'). exec; var command = "mkdir -p '" + newDest + "'"; var options = {}; var après = fonction (erreur, stdout, stderr) {console.log ('erreur', erreur); console.log ('stdout', stdout); console.log ('stderr', stderr); } exec (commande, options, après);
David Silva Smith

24
Cette option peut être interrompue sur les plates-formes node.js qui n'ont pas d'instance mkdir en ligne de commande (c'est-à-dire des hôtes non Linux-y), donc elle n'est pas portable, si cela compte.
cshotton

1
@cshotton - faites-vous référence au commentaire ou à la réponse? shelljs fonctionne même sur Windows. exec mkdir -p (le commentaire) bien sûr ne le fait pas.
bryanmac

Vous pouvez utiliser cette fonction sympa avec Promise ou callback de votre choix.
Илья Зеленько

1
ce n'est pas une solution, c'est une alternative à la solution. contexte: pics.onsizzle.com/…
Nika Kasradze

413

Éditer

La version NodeJS 10.12.0a ajouté un support natif pour les deux mkdiret mkdirSyncpour créer un répertoire de manière récursive avec l' recursive: trueoption comme suit:

fs.mkdirSync(targetDir, { recursive: true });

Et si tu préfères fs Promises API, tu peux écrire

fs.promises.mkdir(targetDir, { recursive: true });

Réponse originale

Créez des répertoires de manière récursive s'ils n'existent pas! ( Zéro dépendances )

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

function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
  const sep = path.sep;
  const initDir = path.isAbsolute(targetDir) ? sep : '';
  const baseDir = isRelativeToScript ? __dirname : '.';

  return targetDir.split(sep).reduce((parentDir, childDir) => {
    const curDir = path.resolve(baseDir, parentDir, childDir);
    try {
      fs.mkdirSync(curDir);
    } catch (err) {
      if (err.code === 'EEXIST') { // curDir already exists!
        return curDir;
      }

      // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
      if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.
        throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
      }

      const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
      if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) {
        throw err; // Throw if it's just the last created dir.
      }
    }

    return curDir;
  }, initDir);
}

Usage

// Default, make directories relative to current working directory.
mkDirByPathSync('path/to/dir');

// Make directories relative to the current script.
mkDirByPathSync('path/to/dir', {isRelativeToScript: true});

// Make directories with an absolute path.
mkDirByPathSync('/path/to/dir');

Démo

Essayez-le!

Explications

  • [UPDATE] Cette solution gère les erreurs spécifiques à la plate-forme comme EISDIRpour Mac et EPERMet EACCESpour Windows. Merci à tous les commentaires de @PediT., @JohnQ, @ deed02392, @robyoder et @Almenon.
  • Cette solution gère à la fois les chemins relatifs et absolus . Merci à @john comment.
  • Dans le cas de chemins relatifs, les répertoires cibles seront créés (résolus) dans le répertoire de travail courant. Pour les résoudre par rapport au répertoire de script actuel, passez {isRelativeToScript: true}.
  • Utiliser path.sepet path.resolve(), pas seulement la /concaténation, pour éviter les problèmes multiplateformes.
  • Utilisation fs.mkdirSyncet gestion de l'erreur avec try/catchif levée pour gérer les conditions de concurrence: un autre processus peut ajouter le fichier entre les appels à fs.existsSync()et fs.mkdirSync()et provoquer une exception.
    • L'autre moyen d'y parvenir pourrait être de vérifier si un fichier existe, puis de le créer, c'est-à-dire if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. Mais c'est un anti-pattern qui laisse le code vulnérable aux conditions de course. Merci au commentaire de @GershomMaes sur la vérification de l'existence du répertoire.
  • Nécessite Node v6 et plus récent pour prendre en charge la déstructuration. (Si vous rencontrez des problèmes pour implémenter cette solution avec d'anciennes versions de Node, laissez-moi un commentaire)

7
Upvote pour la réponse simple et récursive ne nécessitant pas de bibliothèque ou d'approche supplémentaire!
MikingTheViking

1
Instructions requises manquantes: const fs = require ('fs'); chemin const = require ('chemin');
Christopher Bull

1
@ChristopherBull, intentionnellement pas ajouté juste pour se concentrer sur la logique, mais de toute façon, je les ai ajoutés. Thanks;)
Mouneer

1
12 lignes de code plein, zéro dépendances, je le prendrai à chaque fois.
moodboom

1
@Mouneer sous Mac OS X 10.12.6, l'erreur lancée lors de la tentative de création de "/" après avoir passé un chemin absolu est "EISDIR" (Erreur: EISDIR: opération illégale sur un répertoire, mkdir '/'). Je pense que la vérification de l'existence de dir est probablement toujours la meilleure façon de procéder multi-plateforme (en reconnaissant que ce sera plus lent).
John Q

78

Une réponse plus robuste consiste à utiliser use mkdirp .

var mkdirp = require('mkdirp');

mkdirp('/path/to/dir', function (err) {
    if (err) console.error(err)
    else console.log('dir created')
});

Ensuite, écrivez le fichier dans le chemin complet avec:

fs.writeFile ('/path/to/dir/file.dat'....

Préférez cette réponse car vous importez juste ce dont vous avez besoin, pas une bibliothèque entière
Juan Mendes

1
Félicitations pour le badge populiste ;-)
janos

1
Merci. C'est la meilleure méthode.
Stepan Rafael


48

fs-extra ajoute des méthodes de système de fichiers qui ne sont pas incluses dans le module natif fs. C'est une baisse de remplacement pour fs.

Installer fs-extra

$ npm install --save fs-extra

const fs = require("fs-extra");
// Make sure the output directory is there.
fs.ensureDirSync(newDest);

Il existe des options de synchronisation et d'asynchronisation.

https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md


5
C'est la meilleure réponse! La plupart d'entre nous ont déjà fs-extra dans l'application de toute façon.
pagep

Ce serait formidable s'il offrait la possibilité de l'utiliser memfspour des tests unitaires. Ce n'est pas le cas :-( github.com/jprichardson/node-fs-extra/issues/274
schnatterer

31

En utilisant réduire, nous pouvons vérifier si chaque chemin existe et le créer si nécessaire, aussi de cette façon je pense que c'est plus facile à suivre. Modifié, merci @Arvin, nous devrions utiliser path.sep pour obtenir le séparateur de segment de chemin spécifique à la plate-forme.

const path = require('path');

// Path separators could change depending on the platform
const pathToCreate = 'path/to/dir'; 
pathToCreate
 .split(path.sep)
 .reduce((prevPath, folder) => {
   const currentPath = path.join(prevPath, folder, path.sep);
   if (!fs.existsSync(currentPath)){
     fs.mkdirSync(currentPath);
   }
   return currentPath;
 }, '');

4
Lorsque vous donnez une réponse, il est préférable d' expliquer pourquoi votre réponse est la bonne.
Stephen Rauch

Désolé, vous avez raison, je pense que de cette façon c'est plus propre et plus facile à suivre
josebui

4
@josebui Je pense qu'il vaut mieux utiliser "path.sep" au lieu de la barre oblique (/) pour éviter les problèmes spécifiques à l'environnement.
Arvin

bonne solution car ne nécessite pas de nœud> = 10 comme les autres réponses
Karim

29

Cette fonctionnalité a été ajoutée à node.js dans la version 10.12.0, c'est donc aussi simple que de passer une option {recursive: true}comme deuxième argument à l' fs.mkdir()appel. Voir l' exemple dans la documentation officielle .

Pas besoin de modules externes ou de votre propre implémentation.


1
J'ai trouvé la demande d'extraction associée github.com/nodejs/node/pull/23313
nurettin

1
Cela lancera une erreur lorsque le répertoire existera et s'arrêtera. Utiliser un bloc try catch peut lui permettre de continuer à créer un autre dossier inexistant.
Choco Li

1
Cela devrait être la réponse acceptée. Il ne lance pas si le répertoire existe déjà et peut être utilisé avec async / await via fs.promises.mkdir.
Rich Apodaca le

7

Je sais que c'est une vieille question, mais nodejs v10.12.0 le prend désormais en charge de manière native avec l' recursiveoption définie sur true. fs.mkdir

// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});


2

Exemple pour Windows (pas de dépendances supplémentaires et de gestion des erreurs)

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

let dir = "C:\\temp\\dir1\\dir2\\dir3";

function createDirRecursively(dir) {
    if (!fs.existsSync(dir)) {        
        createDirRecursively(path.join(dir, ".."));
        fs.mkdirSync(dir);
    }
}

createDirRecursively(dir); //creates dir1\dir2\dir3 in C:\temp

2

Vous pouvez simplement vérifier que le dossier existe ou non dans le chemin de manière récursive et créer le dossier en vérifiant s'ils ne sont pas présents. ( PAS DE BIBLIOTHÈQUE EXTERNE )

function checkAndCreateDestinationPath (fileDestination) {
    const dirPath = fileDestination.split('/');
    dirPath.forEach((element, index) => {
        if(!fs.existsSync(dirPath.slice(0, index + 1).join('/'))){
            fs.mkdirSync(dirPath.slice(0, index + 1).join('/')); 
        }
    });
}

2

Vous pouvez utiliser la fonction suivante

const recursiveUpload = (chemin: chaîne) => {const chemins = chemin.split ("/")

const fullPath = paths.reduce((accumulator, current) => {
  fs.mkdirSync(accumulator)
  return `${accumulator}/${current}`
  })

  fs.mkdirSync(fullPath)

  return fullPath
}

Alors qu'est-ce que ça fait:

  1. Créez une pathsvariable, où elle stocke chaque chemin par lui-même en tant qu'élément du tableau.
  2. Ajoute "/" à la fin de chaque élément du tableau.
  3. Fait pour le cycle:
    1. Crée un répertoire à partir de la concaténation d'éléments de tableau dont les index vont de 0 à l'itération courante. Fondamentalement, il est récursif.

J'espère que cela pourra aider!

En passant, dans Node v10.12.0, vous pouvez utiliser la création de chemin récursif en le donnant comme argument supplémentaire.

fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { if (err) throw err; });

https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options


1

Trop de réponses, mais voici une solution sans récursivité qui fonctionne en divisant le chemin puis en le reconstruisant de gauche à droite

function mkdirRecursiveSync(path) {
    let paths = path.split(path.delimiter);
    let fullPath = '';
    paths.forEach((path) => {

        if (fullPath === '') {
            fullPath = path;
        } else {
            fullPath = fullPath + '/' + path;
        }

        if (!fs.existsSync(fullPath)) {
            fs.mkdirSync(fullPath);
        }
    });
};

Pour ceux qui sont préoccupés par la compatibilité Windows vs Linux, remplacez simplement la barre oblique par une double barre oblique inverse `` \ '' dans les deux occurrences ci-dessus, mais TBH nous parlons de nœud fs pas de ligne de commande Windows et le premier est assez indulgent et le code ci-dessus fonctionnera simplement sur Windows et est plus une solution multiplateforme complète.


les fichiers sous Windows sont traités avec une barre oblique inverse et non une barre oblique. Votre code ne fonctionnera tout simplement pas là-bas. C: \ data \ test ...
DDD

Modifié mais vous suggère de valider votre commentaire. Sur le nœud, essayez ce qui suit et voyez ce qui se passe var fs = require ('fs') fs.mkdirSync ('test') fs.mkdirSync ('test \\ test1') fs.mkdirSync ('test / test2')
Hamiora

Quoi que vous disiez ..., mon vote négatif reste jusqu'à ce que vous appreniez à écrire un meilleur code.
DDD

Haha. Ok, je vais travailler très dur pour apprendre à écrire un meilleur code. BTW la plupart des réponses ci-dessus, y compris l'OP, utilisent des barres obliques. Je vous suggère d'arrêter de pêcher à la traîne.
Hamiora

1
path.separrive comme / ou \\ pour moi. path.delimiterest: ou;.
Josh Anderson Slate

1
const fs = require('fs');

try {
    fs.mkdirSync(path, { recursive: true });
} catch (error) {
    // this make script keep running, even when folder already exist
    console.log(error);
}

0

Une manière asynchrone de créer des répertoires de manière récursive:

import fs from 'fs'

const mkdirRecursive = function(path, callback) {
  let controlledPaths = []
  let paths = path.split(
    '/' // Put each path in an array
  ).filter(
    p => p != '.' // Skip root path indicator (.)
  ).reduce((memo, item) => {
    // Previous item prepended to each item so we preserve realpaths
    const prevItem = memo.length > 0 ? memo.join('/').replace(/\.\//g, '')+'/' : ''
    controlledPaths.push('./'+prevItem+item)
    return [...memo, './'+prevItem+item]
  }, []).map(dir => {
    fs.mkdir(dir, err => {
      if (err && err.code != 'EEXIST') throw err
      // Delete created directory (or skipped) from controlledPath
      controlledPaths.splice(controlledPaths.indexOf(dir), 1)
      if (controlledPaths.length === 0) {
        return callback()
      }
    })
  })
}

// Usage
mkdirRecursive('./photos/recent', () => {
  console.log('Directories created succesfully!')
})

0

Voici ma version impérative de mkdirpfor nodejs.

function mkdirSyncP(location) {
    let normalizedPath = path.normalize(location);
    let parsedPathObj = path.parse(normalizedPath);
    let curDir = parsedPathObj.root;
    let folders = parsedPathObj.dir.split(path.sep);
    folders.push(parsedPathObj.base);
    for(let part of folders) {
        curDir = path.join(curDir, part);
        if (!fs.existsSync(curDir)) {
            fs.mkdirSync(curDir);
        }
    }
}

0

Que diriez-vous de cette approche:

if (!fs.existsSync(pathToFile)) {
            var dirName = "";
            var filePathSplit = pathToFile.split('/');
            for (var index = 0; index < filePathSplit.length; index++) {
                dirName += filePathSplit[index]+'/';
                if (!fs.existsSync(dirName))
                    fs.mkdirSync(dirName);
            }
        }

Cela fonctionne pour le chemin relatif.


0

Sur la base de la réponse zéro dépendances de mouneer , voici une Typescriptvariante légèrement plus conviviale pour les débutants , en tant que module:

import * as fs from 'fs';
import * as path from 'path';

/**
* Recursively creates directories until `targetDir` is valid.
* @param targetDir target directory path to be created recursively.
* @param isRelative is the provided `targetDir` a relative path?
*/
export function mkdirRecursiveSync(targetDir: string, isRelative = false) {
    const sep = path.sep;
    const initDir = path.isAbsolute(targetDir) ? sep : '';
    const baseDir = isRelative ? __dirname : '.';

    targetDir.split(sep).reduce((prevDirPath, dirToCreate) => {
        const curDirPathToCreate = path.resolve(baseDir, prevDirPath, dirToCreate);
        try {
            fs.mkdirSync(curDirPathToCreate);
        } catch (err) {
            if (err.code !== 'EEXIST') {
                throw err;
            }
            // caught EEXIST error if curDirPathToCreate already existed (not a problem for us).
        }

        return curDirPathToCreate; // becomes prevDirPath on next call to reduce
    }, initDir);
}

0

Aussi propre que ça :)

function makedir(fullpath) {
  let destination_split = fullpath.replace('/', '\\').split('\\')
  let path_builder = destination_split[0]
  $.each(destination_split, function (i, path_segment) {
    if (i < 1) return true
    path_builder += '\\' + path_segment
    if (!fs.existsSync(path_builder)) {
      fs.mkdirSync(path_builder)
    }
  })
}

0

J'ai eu des problèmes avec l'option récursive de fs.mkdir, j'ai donc créé une fonction qui effectue les opérations suivantes:

  1. Crée une liste de tous les répertoires, en commençant par le répertoire cible final et en remontant jusqu'au parent racine.
  2. Crée une nouvelle liste de répertoires nécessaires pour que la fonction mkdir fonctionne
  3. Rend chaque répertoire nécessaire, y compris le fichier final

    function createDirectoryIfNotExistsRecursive(dirname) {
        return new Promise((resolve, reject) => {
           const fs = require('fs');
    
           var slash = '/';
    
           // backward slashes for windows
           if(require('os').platform() === 'win32') {
              slash = '\\';
           }
           // initialize directories with final directory
           var directories_backwards = [dirname];
           var minimize_dir = dirname;
           while (minimize_dir = minimize_dir.substring(0, minimize_dir.lastIndexOf(slash))) {
              directories_backwards.push(minimize_dir);
           }
    
           var directories_needed = [];
    
           //stop on first directory found
           for(const d in directories_backwards) {
              if(!(fs.existsSync(directories_backwards[d]))) {
                 directories_needed.push(directories_backwards[d]);
              } else {
                 break;
              }
           }
    
           //no directories missing
           if(!directories_needed.length) {
              return resolve();
           }
    
           // make all directories in ascending order
           var directories_forwards = directories_needed.reverse();
    
           for(const d in directories_forwards) {
              fs.mkdirSync(directories_forwards[d]);
           }
    
           return resolve();
        });
     }

-1

Exec peut être désordonné sur Windows. Il existe une solution plus «nodie». Fondamentalement, vous avez un appel récursif pour voir si un répertoire existe et plonger dans l'enfant (s'il existe) ou le créer. Voici une fonction qui créera les enfants et appellera une fonction une fois terminé:

fs = require('fs');
makedirs = function(path, func) {
 var pth = path.replace(/['\\]+/g, '/');
 var els = pth.split('/');
 var all = "";
 (function insertOne() {
   var el = els.splice(0, 1)[0];
   if (!fs.existsSync(all + el)) {
    fs.mkdirSync(all + el);
   }
   all += el + "/";
   if (els.length == 0) {
    func();
   } else {
     insertOne();
   }
   })();

}


-1

Cette version fonctionne mieux sur Windows que la réponse principale car elle comprend les deux /et path.sepque les barres obliques fonctionnent comme elles le devraient sur Windows. Prend en charge les chemins absolus et relatifs (par rapport au process.cwd).

/**
 * Creates a folder and if necessary, parent folders also. Returns true
 * if any folders were created. Understands both '/' and path.sep as 
 * path separators. Doesn't try to create folders that already exist,
 * which could cause a permissions error. Gracefully handles the race 
 * condition if two processes are creating a folder. Throws on error.
 * @param targetDir Name of folder to create
 */
export function mkdirSyncRecursive(targetDir) {
  if (!fs.existsSync(targetDir)) {
    for (var i = targetDir.length-2; i >= 0; i--) {
      if (targetDir.charAt(i) == '/' || targetDir.charAt(i) == path.sep) {
        mkdirSyncRecursive(targetDir.slice(0, i));
        break;
      }
    }
    try {
      fs.mkdirSync(targetDir);
      return true;
    } catch (err) {
      if (err.code !== 'EEXIST') throw err;
    }
  }
  return false;
}

Le vote négatif était-il pour la prise en charge de Windows correctement? Ai-je mentionné que cela fonctionne également sur d'autres systèmes d'exploitation?
Qwertie
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.