¿Cómo crear una ruta completa con el nodo fs.mkdirSync?


159

Estoy tratando de crear una ruta completa si no existe.

El código se ve así:

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

Este código funciona muy bien siempre que solo haya un subdirectorio (un nuevoDest como 'dir1'), sin embargo, cuando hay una ruta de directorio como ('dir1 / dir2') falla con Error: ENOENT, no existe tal archivo o directorio

Me gustaría poder crear la ruta completa con tan pocas líneas de código como sea necesario.

Leí que hay una opción recursiva en fs y lo intenté así

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

Siento que debería ser así de simple crear recursivamente un directorio que no existe. ¿Me estoy perdiendo algo o necesito analizar la ruta y verificar cada directorio y crearlo si aún no existe?

Soy bastante nuevo en Node. Tal vez estoy usando una versión anterior de FS?



44
@AndyRay Esta pregunta de StackOverflow ahora es el mejor resultado en Google para esta pregunta, lo cual es divertido porque eso significa que es recurrente ...
Matt Parkins

1
Ese era un problema en las versiones anteriores de Node, la actualización al Nodo 12+ resuelve el problema
MrJomp

Respuestas:


48

Una opción es usar el módulo shelljs

npm install shelljs

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

Desde esa página:

Opciones Disponibles:

p: ruta completa (creará directorios intermedios si es necesario)

Como otros han notado, hay otros módulos más enfocados. Pero, fuera de mkdirp, tiene toneladas de otras operaciones de shell útiles (como cuál, grep, etc.) y funciona en Windows y * nix


2
¡Gracias! Terminé usando exec (ya estaba usando esto) y funcionó de maravilla. var exec = require ('child_process'). exec; comando var = "mkdir -p '" + newDest + "'"; opciones de var = {}; var after = function (error, stdout, stderr) {console.log ('error', error); console.log ('stdout', stdout); console.log ('stderr', stderr); } exec (comando, opciones, después);
David Silva Smith

24
Esta opción puede romperse en plataformas node.js que no tienen una instancia de línea de comando mkdir (es decir, hosts que no sean Linux-y), por lo que no es portátil, si eso es importante.
cshotton

1
@cshotton: ¿te refieres al comentario o la respuesta? Shelljs funciona incluso en Windows. exec mkdir -p (el comentario), por supuesto, no.
bryanmac

Puede utilizar esta función genial con Promesa o devolución de llamada de su elección.
Илья Зеленько

1
Esto no es una solución, es una alternativa a la solución. context: pics.onsizzle.com/…
Nika Kasradze

413

Editar

La versión NodeJS 10.12.0ha agregado un soporte nativo para ambos mkdiry mkdirSyncpara crear un directorio recursivamente con la recursive: trueopción de la siguiente manera:

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

Y si lo prefieres fs Promises API, puedes escribir

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

Respuesta original

¡Cree directorios recursivamente si no existen! ( Cero dependencias )

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

Uso

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

Manifestación

¡Intentalo!

Explicaciones

  • [ACTUALIZACIÓN] Esta solución maneja errores específicos de la plataforma como EISDIRpara Mac EPERMyEACCES para Windows. Gracias a todos los comentarios de informes de @PediT., @JohnQ, @ deed02392, @robyoder y @Almenon.
  • Esta solución maneja tanto relativos como absolutos. rutas . Gracias al comentario de @john.
  • En el caso de rutas relativas, los directorios de destino se crearán (resolverán) en el directorio de trabajo actual. Para resolverlos en relación con el directorio de script actual, pase{isRelativeToScript: true} .
  • Uso path.sepy path.resolve(), no solo /concatenación, para evitar problemas entre plataformas.
  • El uso fs.mkdirSyncy manejo del error con try/catchsi se tiran para manejar las condiciones de carrera: otro proceso puede agregar el archivo entre las llamadas a fs.existsSync()y fs.mkdirSync()y provoca una excepción.
    • La otra manera de lograr que se podría comprobar si un archivo existe, entonces la creación de ella, es decir, if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. Pero este es un antipatrón que deja el código vulnerable a las condiciones de carrera. Gracias al comentario de @GershomMaes sobre la verificación de existencia del directorio.
  • Requiere Nodo v6 y posterior para soportar la desestructuración. (Si tiene problemas para implementar esta solución con versiones antiguas de Node, solo déjeme un comentario)

77
¡Vota por la respuesta fácil y recursiva que no requiere una biblioteca o enfoque adicional!
MikingTheViking

1
Faltan sentencias require: const fs = require ('fs'); const path = require ('ruta');
Christopher Bull

1
@ChristopherBull, intencionalmente no agregado solo para enfocarme en la lógica, pero de todos modos, los agregué. Gracias;)
Mouneer

1
12 líneas de código sólido, cero dependencias, lo tomaré siempre.
moodboom

1
@Mouneer en Mac OS X 10.12.6, el error arrojado al intentar crear "/" después de pasar una ruta absoluta es "EISDIR" (Error: EISDIR: operación ilegal en un directorio, mkdir '/'). Creo que probablemente verificar la existencia de directorios sigue siendo la mejor forma de hacerlo en varias plataformas (reconociendo que será más lento).
John Q

78

Una respuesta más robusta es usar use mkdirp .

var mkdirp = require('mkdirp');

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

Luego proceda a escribir el archivo en la ruta completa con:

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

Prefiere esta respuesta ya que está importando justo lo que necesita, no una biblioteca completa
Juan Mendes

1
Felicidades por la insignia populista ;-)
janos

1
Gracias. Es el mejor método.
Stepan Rafael


48

fs-extra agrega métodos del sistema de archivos que no están incluidos en el módulo fs nativo. Es una caída en el reemplazo de fs.

Instalar en pc fs-extra

$ npm install --save fs-extra

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

Hay opciones de sincronización y asíncrona.

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


55
¡Esta es la mejor respuesta! La mayoría de nosotros ya tenemos fs-extra en la aplicación de todos modos.
pagep

Esto sería genial si ofreciera la posibilidad de usar memfspara pruebas unitarias. No lo hace :-( github.com/jprichardson/node-fs-extra/issues/274
schnatterer

31

Usando reduce podemos verificar si cada ruta existe y crearla si es necesario, también de esta manera creo que es más fácil de seguir. Editado, gracias @Arvin, deberíamos usar path.sep para obtener el separador de segmento de ruta específico de la plataforma adecuada.

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

44
Al dar una respuesta, es preferible dar una explicación de POR QUÉ su respuesta es la correcta.
Stephen Rauch

Lo siento, tienes razón, creo que de esta manera es más limpio y más fácil de seguir
josebui

44
@josebui Creo que es mejor usar "path.sep" en lugar de la barra diagonal (/) para evitar problemas específicos del entorno.
Arvin

buena solución porque no requiere nodo> = 10 como las otras respuestas
Karim

29

Esta característica se ha agregado a node.js en la versión 10.12.0, por lo que es tan fácil como pasar una opción {recursive: true}como segundo argumento a la fs.mkdir()llamada. Vea el ejemplo en los documentos oficiales. .

No necesita módulos externos ni su propia implementación.


1
Encontré la solicitud de extracción relacionada github.com/nodejs/node/pull/23313
nurettin

1
Lanzará un error cuando exista el directorio y se detendrá. El uso de un bloque try catch puede hacer que siga creando otra carpeta no existente.
Choco Li

1
Esta debería ser la respuesta aceptada. No se lanza si el directorio ya existe, y se puede usar con async / await a través de fs.promises.mkdir.
Rich Apodaca

7

Sé que esta es una pregunta anterior, pero nodejs v10.12.0 ahora admite esto de forma nativa con la recursiveopción establecida en verdadero. 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

Ejemplo para Windows (sin dependencias adicionales y manejo de errores)

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

Simplemente puede verificar que la carpeta exista o no en la ruta de forma recursiva y crear la carpeta mientras verifica si no están presentes. ( SIN BIBLIOTECA EXTERNA )

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

Puedes usar la siguiente función

const recursiveUpload = (ruta: cadena) => {rutas const = ruta.split ("/")

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

  fs.mkdirSync(fullPath)

  return fullPath
}

Entonces, qué hace:

  1. Crear paths variable, donde almacena cada ruta por sí misma como un elemento de la matriz.
  2. Agrega "/" al final de cada elemento en la matriz.
  3. Hace para el ciclo:
    1. Crea un directorio a partir de la concatenación de elementos de matriz cuyos índices son de 0 a la iteración actual. Básicamente, es recursivo.

¡Espero que ayude!

Por cierto, en el Nodo v10.12.0 puede usar la creación de ruta recursiva dándola como argumento adicional.

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

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


1

Demasiadas respuestas, pero aquí hay una solución sin recurrencia que funciona dividiendo el camino y luego volviendo a construirlo de izquierda a derecha

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

Para aquellos preocupados por la compatibilidad entre Windows y Linux, simplemente reemplace la barra diagonal con doble barra diagonal inversa '\' en los dos casos anteriores, pero TBH estamos hablando de nodo fs no de línea de comando de Windows y el primero es bastante indulgente y el código anterior simplemente funcionará en Windows y es más una solución completa multiplataforma.


Los archivos en Windows se manejan con barra invertida, no barra inclinada. Su código simplemente no funcionará allí. C: \ data \ test ...
DDD

Editado pero le sugiero que valide su comentario. En el nodo, intente lo siguiente y vea qué sucede var fs = require ('fs') fs.mkdirSync ('test') fs.mkdirSync ('test \\ test1') fs.mkdirSync ('test / test2')
Hamiora

Lo que sea que estés diciendo ..., mi voto negativo aún permanece hasta que aprendas a escribir un mejor código.
DDD

Jaja. Ok, trabajaré muy duro para aprender a escribir un mejor código. Por cierto, la mayoría de las respuestas anteriores, incluido el OP, usan barras diagonales. Te sugiero que dejes de trollear.
Hamiora

1
path.sepestá llegando como / o \\ para mí. path.delimiteres: o;.
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

Una forma asincrónica de crear directorios de forma recursiva:

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

Aquí está mi versión imprescindible de mkdirppara 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

¿Qué tal este enfoque:

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

Esto funciona para la ruta relativa.


0

Basado en la respuesta de cero dependencias de Mouneer , aquí hay una Typescriptvariante un poco más amigable para principiantes , como módulo:

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

Tan limpio como esto :)

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

Tuve problemas con la opción recursiva de fs.mkdir, así que hice una función que hace lo siguiente:

  1. Crea una lista de todos los directorios, comenzando con el directorio de destino final y trabajando hasta el padre raíz.
  2. Crea una nueva lista de directorios necesarios para que funcione la función mkdir
  3. Hace que cada directorio sea necesario, incluido el 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 puede ser desordenado en Windows. Hay una solución más "nodie". Básicamente, tiene una llamada recursiva para ver si existe un directorio y sumergirse en el elemento secundario (si existe) o crearlo. Aquí hay una función que creará los hijos y llamará a una función cuando termine:

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

Esta versión funciona mejor en Windows que la respuesta principal porque comprende ambos /y path.seppara que las barras diagonales funcionen en Windows como deberían. Admite rutas absolutas y relativas (relativas a 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;
}

¿Fue el voto negativo para apoyar Windows correctamente? ¿Mencioné que también funciona en otros sistemas operativos?
Qwertie
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.