web-dev-qa-db-fra.com

Comment créer un chemin complet avec le fichier fs.mkdirSync du noeud?

J'essaie 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'). Cependant, lorsqu'il existe un chemin de répertoire du type ('dir1/dir2'), il échoue avec Erreur: ENOENT, aucun fichier de ce type 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 je l'ai essayé comme ça

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

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

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

98
David Silva Smith

Une option consiste à utiliser shelljs module

npm installer shelljs

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

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 grep, etc.) et fonctionne avec windows et * nix

45
bryanmac

Modifier

NodeJS version 10 a ajouté une prise en charge native de mkdir et mkdirSync afin de créer un répertoire de manière récursive avec l'option recursive: true comme suit:

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

Et si vous préférez fs Promises API , vous pouvez é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! (Zero dependencies)

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!

Des explications

  • [UPDATE] Cette solution gère les erreurs propres à la plate-forme, telles que EISDIR pour Mac et EPERM et EACCES pour Windows. Merci à tous les commentaires de @PediT., @JohnQ, @ deed02392, @robyoder et @Almenon.
  • Cette solution gère les deux chemins relative et absolu. Merci à @john commenter.
  • Dans le cas de chemins relatifs, les répertoires cibles seront créés (résolus) dans le répertoire de travail actuel. Pour les résoudre par rapport au répertoire de script actuel, transmettez {isRelativeToScript: true}.
  • Utilisation de path.sep et path.resolve() , et pas seulement de la concaténation /, pour éviter les problèmes multi-plateformes.
  • Utilisation de fs.mkdirSync et gestion de l'erreur avec try/catch si le problème est traité: un autre processus peut ajouter le fichier entre les appels à fs.existsSync() et fs.mkdirSync() et provoque 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-modèle qui laisse le code vulnérable aux conditions de concurrence. Merci à @GershomMaes de commenter le contrôle d’existence des répertoires.
  • Nécessite Node v6 et plus récent pour la déstructuration. (Si vous rencontrez des problèmes pour implémenter cette solution avec d'anciennes versions de Node, laissez-moi un commentaire.)
153
Mouneer

Une réponse plus robuste consiste à utiliser 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'....
69
cshotton

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

Installez 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 async.

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

42
Deejers

En utilisant reduction, 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;
 }, '');
29
josebui

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

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

25
Capaj

je sais que c’est une vieille question, mais nodejs v10.12 le prend désormais en charge de manière native avec l’option recursive 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;
});
4
Nelson Owalo

Vous pouvez utiliser la fonction suivante

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

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

  fs.mkdirSync(fullPath)

  return fullPath
}

Alors qu'est-ce qu'il fait:

  1. Créez la variable paths, 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 actuelle. Fondamentalement, il est récursif.

J'espère que cela pourra aider!

En passant, dans Node 10.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

2
hypeofpipe

Trop de réponses, mais voici une solution sans récursivité qui fonctionne en scindant 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 s'inquiètent de la compatibilité Windows vs Linux, il suffit de remplacer la barre oblique par une double barre oblique inversée '\' dans les deux occurrences ci-dessus, mais TBH concerne le noeud fs et non la ligne de commande Windows. fonctionnera simplement sous Windows et constituera davantage une solution complète multiplateforme.

1
Hamiora

Exemple pour Windows (pas de dépendances supplémentaires et traitement 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
1
Andrey Imshenik

Maintenant avec NodeJS> = 10, vous pouvez utiliser fs.mkdirSync(path, { recursive: true })fs.mkdirSync

1
William Penagos
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);
}
1
Choco Li

Sur la base de la réponse de mouneer zéro-dependencies, voici une variante TypeScript légèrement plus conviviale pour les débutants, sous forme de 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
brokenthorn

Vous pouvez simplement vérifier que le dossier existe ou non dans le chemin d'accès de manière récursive et créer le dossier au fur et à mesure que vous vérifiez s'il est absent. ( PAS DE BIBLIOTHEQUE 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('/')); 
        }
    });
}
0
Pulkit Aggarwal

Voici ma version impérative de mkdirp pour 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
ubershmekel

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
muratgozel

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
Gouravmoy Mohanty

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
Bob Vandevliet