Wie erstelle ich einen vollständigen Pfad mit fs.mkdirSync des Knotens?

159

Ich versuche, einen vollständigen Pfad zu erstellen, wenn er nicht vorhanden ist.

Der Code sieht folgendermaßen aus:

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

Dieser Code funktioniert hervorragend, solange es nur ein Unterverzeichnis gibt (ein newDest wie 'dir1'). Wenn es jedoch einen Verzeichnispfad wie ('dir1 / dir2') gibt, schlägt er mit Fehler: ENOENT fehl, keine solche Datei oder kein solches Verzeichnis

Ich möchte in der Lage sein, den vollständigen Pfad mit so wenigen Codezeilen wie nötig zu erstellen.

Ich habe gelesen, dass es eine rekursive Option für fs gibt und habe es so versucht

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

Ich denke, es sollte so einfach sein, rekursiv ein Verzeichnis zu erstellen, das es nicht gibt. Fehlt mir etwas oder muss ich den Pfad analysieren, jedes Verzeichnis überprüfen und erstellen, falls es noch nicht vorhanden ist?

Ich bin ziemlich neu bei Node. Vielleicht verwende ich eine alte Version von FS?

David Silva Smith
quelle
1
github.com/substack/node-mkdirp und alle möglichen anderen Lösungen in dieser Google-Suche .
jfriend00
4
@AndyRay Diese StackOverflow-Frage ist jetzt das Top-Ergebnis in Google für diese Frage, was lustig ist, weil das bedeutet, dass es rekursiv ist ....
Matt Parkins
1
Das war ein Problem bei älteren Versionen von Node. Ein Update auf Node 12+ löst das Problem
MrJomp

Antworten:

48

Eine Möglichkeit ist die Verwendung des ShellJS-Moduls

npm installiere shelljs

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

Von dieser Seite:

Verfügbare Optionen:

p: vollständiger Pfad (erstellt bei Bedarf Zwischenverzeichnisse)

Wie andere angemerkt haben, gibt es andere fokussiertere Module. Außerhalb von mkdirp gibt es jedoch unzählige andere nützliche Shell-Operationen (wie welche, grep usw.) und es funktioniert unter Windows und * nix

Bryanmac
quelle
2
Vielen Dank! Am Ende habe ich exec verwendet (ich habe dies bereits verwendet) und es hat wie ein Zauber funktioniert. var exec = require ('child_process'). exec; var command = "mkdir -p '" + newDest + "'"; var options = {}; var after = function (Fehler, stdout, stderr) {console.log ('Fehler', Fehler); console.log ('stdout', stdout); console.log ('stderr', stderr); } exec (Befehl, Optionen, danach);
David Silva Smith
24
Diese Option kann auf node.js-Plattformen ohne Befehlszeilen-mkdir-Instanz (dh Nicht-Linux-y-Hosts) unterbrochen werden, sodass sie nicht portierbar ist, wenn dies wichtig ist.
Cshotton
1
@cshotton - beziehen Sie sich auf den Kommentar oder die Antwort? shelljs funktioniert auch unter Windows. exec mkdir -p (der Kommentar) natürlich nicht.
Bryanmac
Sie können diese coole Funktion mit Promise oder Callback Ihrer Wahl verwenden.
Злья Зеленько
1
Dies ist keine Lösung, dies ist eine Alternative zur Lösung. Kontext: pics.onsizzle.com/…
Nika Kasradze
412

Bearbeiten

Die NodeJS-Version 10.12.0hat eine native Unterstützung für beide hinzugefügt mkdirund mkdirSynczum rekursiven Erstellen eines Verzeichnisses mit recursive: trueder folgenden Option:

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

Und wenn Sie es vorziehen fs Promises API, können Sie schreiben

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

Ursprüngliche Antwort

Erstellen Sie Verzeichnisse rekursiv, wenn sie nicht vorhanden sind! ( Null Abhängigkeiten )

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);
}

Verwendung

// 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');

Demo

Versuch es!

Erklärungen

  • [UPDATE] Diese Lösung Griffe plattformspezifische Fehler wie EISDIRfür Mac und EPERMund EACCESfür Windows. Vielen Dank an alle Berichtskommentare von @PediT., @JohnQ, @ deed02392, @robyoder und @Almenon.
  • Diese Lösung verarbeitet sowohl relative als auch absolute Pfade. Danke an @john Kommentar.
  • Bei relativen Pfaden werden Zielverzeichnisse im aktuellen Arbeitsverzeichnis erstellt (aufgelöst). Übergeben Sie, um sie relativ zum aktuellen Skriptverzeichnis aufzulösen {isRelativeToScript: true}.
  • Verwenden path.sepund path.resolve()nicht nur /Verketten, um plattformübergreifende Probleme zu vermeiden.
  • Verwenden fs.mkdirSyncund Behandeln des Fehlers mit, try/catchwenn er ausgelöst wird, um die Rennbedingungen zu behandeln: Ein anderer Prozess kann die Datei zwischen den Aufrufen von fs.existsSync()und hinzufügen fs.mkdirSync()und eine Ausnahme verursachen.
    • Der andere Weg, dies zu erreichen, könnte darin bestehen, zu überprüfen, ob eine Datei vorhanden ist, und sie dann zu erstellen, d if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. H. Dies ist jedoch ein Anti-Pattern, das den Code für Rennbedingungen anfällig macht. Dank @GershomMaes Kommentar zur Verzeichnis-Existenzprüfung.
  • Benötigt Node v6 und neuer, um die Destrukturierung zu unterstützen. (Wenn Sie Probleme bei der Implementierung dieser Lösung mit alten Node-Versionen haben, hinterlassen Sie mir einfach einen Kommentar.)
Mouneer
quelle
7
Upvote für die einfache, rekursive Antwort, für die keine zusätzliche Bibliothek oder Vorgehensweise erforderlich ist!
MikingTheViking
1
Fehlende require-Anweisungen: const fs = require ('fs'); const path = require ('path');
Christopher Bull
1
@ChristopherBull, absichtlich nicht nur hinzugefügt, um sich auf die Logik zu konzentrieren, sondern trotzdem, ich habe sie hinzugefügt. Danke;)
Mouneer
1
12 Zeilen Solid Code, keine Abhängigkeiten, ich nehme es jedes Mal.
Moodboom
1
@Mouneer unter Mac OS X 10.12.6 lautet der Fehler beim Versuch, "/" nach Übergabe eines absoluten Pfads zu erstellen, "EISDIR" (Fehler: EISDIR: Unzulässiger Vorgang in einem Verzeichnis, mkdir '/'). Ich denke, wahrscheinlich ist die Überprüfung der Existenz von dir immer noch der beste plattformübergreifende Weg (zu erkennen, dass es langsamer sein wird).
John Q
78

Eine robustere Antwort ist die Verwendung von mkdirp .

var mkdirp = require('mkdirp');

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

Schreiben Sie dann die Datei in den vollständigen Pfad mit:

fs.writeFile ('/path/to/dir/file.dat'....
cshotton
quelle
Bevorzugen Sie diese Antwort, da Sie genau das importieren, was Sie benötigen, nicht eine ganze Bibliothek
Juan Mendes
1
Herzlichen Glückwunsch zum populistischen Abzeichen ;-)
Janos
1
Vielen Dank. Es ist die weitaus beste Methode.
Stepan Rafael
48

fs-extra fügt Dateisystemmethoden hinzu, die nicht im nativen fs-Modul enthalten sind. Es ist ein Rückgang des Ersatzes für fs.

Installieren fs-extra

$ npm install --save fs-extra

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

Es gibt Synchronisierungs- und Asynchronisierungsoptionen.

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

Deejers
quelle
5
Das ist die beste Antwort! Die meisten von uns haben sowieso schon fs-extra in der App.
Seite
Dies wäre großartig, wenn es die Möglichkeit bieten würde, es memfsfür Unit-Tests zu verwenden. Es tut nicht :-( github.com/jprichardson/node-fs-extra/issues/274
Schnatterer
31

Mit reduzieren können wir überprüfen, ob jeder Pfad vorhanden ist, und ihn bei Bedarf erstellen. Auch auf diese Weise ist es meiner Meinung nach einfacher, ihm zu folgen. Dank @Arvin bearbeitet, sollten wir path.sep verwenden, um das richtige plattformspezifische Trennzeichen für Pfadsegmente zu erhalten.

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;
 }, '');
Josebui
quelle
4
Wenn Sie eine Antwort geben, ist es vorzuziehen, eine Erklärung zu geben , WARUM Ihre Antwort diejenige ist.
Stephen Rauch
Entschuldigung, Sie haben Recht, ich denke auf diese Weise ist es sauberer und einfacher zu folgen
Josebu
4
@josebui Ich denke, es ist besser, "path.sep" anstelle eines Schrägstrichs (/) zu verwenden, um umgebungsspezifische Probleme zu vermeiden.
Arvin
Gute Lösung, da kein Knoten> = 10 wie bei den anderen Antworten erforderlich ist
Karim
29

Diese Funktion wurde in Version 10.12.0 zu node.js hinzugefügt, sodass es so einfach ist, eine Option {recursive: true}als zweites Argument an den fs.mkdir()Aufruf zu übergeben. Siehe das Beispiel in den offiziellen Dokumenten .

Keine Notwendigkeit für externe Module oder Ihre eigene Implementierung.

Capaj
quelle
1
Ich fand die entsprechende Pull-Anfrage github.com/nodejs/node/pull/23313
nurettin
1
Es wird ein Fehler ausgegeben, wenn ein Verzeichnis vorhanden ist, und wird gestoppt. Die Verwendung eines try catch-Blocks kann dazu führen, dass weiterhin andere nicht vorhandene Ordner erstellt werden.
Choco Li
1
Dies sollte die akzeptierte Antwort sein. Es wird nicht ausgelöst, wenn das Verzeichnis bereits vorhanden ist, und kann mit async / await über fs.promises.mkdir verwendet werden.
Rich Apodaca
7

Ich weiß, dass dies eine alte Frage ist, aber nodejs v10.12.0 unterstützt dies jetzt nativ mit der recursiveOption 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;
});
Nelson Owalo
quelle
6

Jetzt mit NodeJS> = 10.12.0, können Sie fs.mkdirSync(path, { recursive: true }) fs.mkdirSync

William Penagos
quelle
2

Beispiel für Windows (keine zusätzlichen Abhängigkeiten und Fehlerbehandlung)

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
Andrey Imshenik
quelle
2

Sie können einfach rekursiv überprüfen, ob ein Ordner vorhanden ist oder nicht, und den Ordner erstellen, während Sie prüfen, ob er nicht vorhanden ist. ( KEINE EXTERNE BIBLIOTHEK )

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('/')); 
        }
    });
}
Pulkit Aggarwal
quelle
2

Sie können die nächste Funktion verwenden

const recursiveUpload = (Pfad: Zeichenfolge) => {const path = path.split ("/")

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

  fs.mkdirSync(fullPath)

  return fullPath
}

Also, was es tut:

  1. Erstellen Sie eine pathsVariable, in der jeder Pfad für sich als Element des Arrays gespeichert wird.
  2. Fügt am Ende jedes Elements im Array "/" hinzu.
  3. Macht für den Zyklus:
    1. Erstellt ein Verzeichnis aus der Verkettung von Array-Elementen, deren Indizes von 0 bis zur aktuellen Iteration reichen. Grundsätzlich ist es rekursiv.

Hoffentlich hilft das!

Übrigens können Sie in Node v10.12.0 die rekursive Pfaderstellung verwenden, indem Sie sie als zusätzliches Argument angeben.

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

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


1

Zu viele Antworten, aber hier ist eine Lösung ohne Rekursion, bei der der Pfad aufgeteilt und dann von links nach rechts wieder aufgebaut wird

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);
        }
    });
};

Für diejenigen, die sich Gedanken über die Kompatibilität von Windows und Linux machen, ersetzen Sie einfach den Schrägstrich durch doppelten Backslash '\' in beiden oben genannten Fällen, aber TBH, wir sprechen über den Knoten fs, nicht über die Windows-Befehlszeile, und der erstere ist ziemlich verzeihend, und der obige Code funktioniert einfach Windows und ist eher eine plattformübergreifende Komplettlösung.


Dateien unter Windows werden mit Backslash und nicht mit Forward Slash behandelt. Ihr Code funktioniert dort einfach nicht. C: \ data \ test ...
DDD

Bearbeitet, aber vorschlagen, dass Sie Ihren Kommentar bestätigen. Versuchen Sie auf dem Knoten Folgendes und sehen Sie, was passiert. Var fs = require ('fs') fs.mkdirSync ('test') fs.mkdirSync ('test \\ test1') fs.mkdirSync ('test / test2')
Hamiora

Was auch immer Sie sagen ... meine Abstimmung bleibt so lange bestehen, bis Sie lernen, besseren Code zu schreiben.
DDD

Haha. Ok, ich werde wirklich hart daran arbeiten zu lernen, wie man besseren Code schreibt. Übrigens verwenden die meisten Antworten oben, einschließlich des OP, Schrägstriche. Schlagen Sie vor, dass Sie aufhören zu trollen.
Hamiora

1
path.sepkommt als entweder / oder \\ für mich durch. path.delimiterist oder ;.
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

Eine asynchrone Methode zum rekursiven Erstellen von Verzeichnissen:

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

Hier ist meine zwingende Version von mkdirpfür 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

Wie wäre es mit diesem Ansatz:

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);
            }
        }

Dies funktioniert für den relativen Pfad.


0

Basierend auf der Null-Abhängigkeits-Antwort von mouneer gibt es hier eine etwas anfängerfreundlichere TypescriptVariante als Modul:

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

So sauber wie das :)

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

Ich hatte Probleme mit der rekursiven Option von fs.mkdir, also habe ich eine Funktion erstellt, die Folgendes ausführt:

  1. Erstellt eine Liste aller Verzeichnisse, beginnend mit dem endgültigen Zielverzeichnis und bis zum übergeordneten Stammverzeichnis.
  2. Erstellt eine neue Liste der benötigten Verzeichnisse, damit die Funktion mkdir funktioniert
  3. Macht jedes benötigte Verzeichnis, einschließlich des Finales

    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 kann unter Windows chaotisch sein. Es gibt eine "Nodie" -Lösung. Grundsätzlich haben Sie einen rekursiven Aufruf, um festzustellen, ob ein Verzeichnis vorhanden ist, und in das untergeordnete Verzeichnis einzutauchen (falls vorhanden) oder es zu erstellen. Hier ist eine Funktion, die die untergeordneten Elemente erstellt und nach Abschluss eine Funktion aufruft:

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();
   }
   })();

}}

Greg S.
quelle
-1

Diese Version funktioniert unter Windows besser als die Top-Antwort, da sie beide versteht /und path.sepSchrägstriche unter Windows so funktionieren, wie sie sollten. Unterstützt absolute und relative Pfade (relativ zu 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;
}
Qwertie
quelle
War die Ablehnung für die korrekte Unterstützung von Windows richtig? Habe ich schon erwähnt, dass es auch auf anderen Betriebssystemen funktioniert?
Qwertie