¿Cómo crear un directorio si no existe usando Node.js?


Respuestas:


1281
var fs = require('fs');
var dir = './tmp';

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

28
Si está realizando esta operación en el arranque o la inicialización de la aplicación, entonces está bien bloquear la ejecución, ya que haría lo mismo si fuera asíncrono. Si está haciendo un directorio como una operación recurrente, entonces es una mala práctica, pero probablemente no causará ningún problema de rendimiento, pero no obstante es un mal hábito. Úselo solo para arrancar su aplicación o para operaciones de una sola vez.
tsturzl

20
existSync () no está en desuso, aunque existe () es - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick

el uso de Syncmétodos * suele ser un no-no: no quiero bloquear el bucle de eventos
Max Heiber

14
Usar métodos de sincronización está bien para scripts locales y demás, obviamente no es una buena idea para un servidor.
Pier

Si ese bloque está rodeado por setTimeout, es asíncrono .....................
Bryan Grace

186

No, por múltiples razones.

  1. El pathmódulo no tiene un método exists/ existsSync. Está en el fsmódulo. (¿Quizás acabas de hacer un error tipográfico en tu pregunta?)

  2. Los documentos te desalientan explícitamente de usar exists.

    fs.exists()es un anacronismo y existe solo por razones históricas. Casi nunca debería haber una razón para usarlo en su propio código.

    En particular, verificar si existe un archivo antes de abrirlo es un antipatrón que lo deja vulnerable a las condiciones de carrera: otro proceso puede eliminar el archivo entre las llamadas a fs.exists()y fs.open(). Simplemente abra el archivo y maneje el error cuando no esté allí.

    Como estamos hablando de un directorio en lugar de un archivo, este consejo implica que debe llamar mkdire ignorar incondicionalmente EEXIST.

  3. En general, debe evitar los Syncmétodos * . Están bloqueando, lo que significa que nada más en su programa puede suceder mientras va al disco. Esta es una operación muy costosa, y el tiempo que toma rompe el supuesto central del bucle de eventos del nodo.

    Los Syncmétodos * generalmente están bien en scripts rápidos de un solo propósito (aquellos que hacen una cosa y luego salen), pero casi nunca se deben usar cuando está escribiendo un servidor: su servidor no podrá responder a nadie durante toda la duración de las solicitudes de E / S. Si varias solicitudes de clientes requieren operaciones de E / S, su servidor se detendrá rápidamente.


    La única vez que consideraría usar Syncmétodos * en una aplicación de servidor es en una operación que ocurre una vez (y solo una vez), al inicio. Por ejemplo, en require realidad se usareadFileSync para cargar módulos.

    Incluso entonces, debe tener cuidado porque muchas E / S sincrónicas pueden ralentizar innecesariamente el tiempo de inicio de su servidor.


    En su lugar, debe usar los métodos de E / S asíncronas.

Entonces, si juntamos esos consejos, obtenemos algo como esto:

function ensureExists(path, mask, cb) {
    if (typeof mask == 'function') { // allow the `mask` parameter to be optional
        cb = mask;
        mask = 0777;
    }
    fs.mkdir(path, mask, function(err) {
        if (err) {
            if (err.code == 'EEXIST') cb(null); // ignore the error if the folder already exists
            else cb(err); // something else went wrong
        } else cb(null); // successfully created folder
    });
}

Y podemos usarlo así:

ensureExists(__dirname + '/upload', 0744, function(err) {
    if (err) // handle folder creation error
    else // we're all good
});

Por supuesto, esto no tiene en cuenta los casos extremos como

  • ¿Qué sucede si la carpeta se elimina mientras se ejecuta el programa? (suponiendo que solo verifique que existe una vez durante el inicio)
  • ¿Qué sucede si la carpeta ya existe pero tiene los permisos incorrectos?

1
¿hay alguna manera de evitar SyntaxError: los literales octales no están permitidos en modo estricto?
Whisher

8
Escríbelo como un decimal. 0744 == 484.
josh3736

3
Una alternativa es utilizar un módulo que amplíe fs para tener esta funcionalidad, como github.com/jprichardson/node-fs-extra
Bret

¿Esta bandera de "máscara" sigue siendo relevante en 2019? ¿Cuál fue el propósito de esto?
oldboy

Es el modo de archivo Unix : los permisos de lectura / escritura del directorio.
josh3736


34

El mkdirmétodo tiene la capacidad de crear recursivamente cualquier directorio en una ruta que no exista e ignorar los que sí existen.

Desde el Nodo v10 / 11 documentos :

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

NOTA: fsPrimero deberá importar el módulo incorporado .

Ahora, aquí hay un ejemplo un poco más robusto que aprovecha los módulos ES nativos (con la marca habilitada y la extensión .mjs), maneja rutas no root y da cuenta de las rutas completas:

import fs from 'fs';
import path from 'path';

createDirectories(pathname) {
   const __dirname = path.resolve();
   pathname = pathname.replace(/^\.*\/|\/?[^\/]+\.[a-z]+|\/$/g, ''); // Remove leading directory markers, and remove ending /file-name.extension
   fs.mkdir(path.resolve(__dirname, pathname), { recursive: true }, e => {
       if (e) {
           console.error(e);
       } else {
           console.log('Success');
       }
    });
}

Puedes usarlo como createDirectories('/components/widget/widget.js');.

Y, por supuesto, es probable que desee obtener más fantasía mediante el uso de promesas con async / await para aprovechar la creación de archivos de una manera sincrónica más legible cuando se crean los directorios; pero, eso está más allá del alcance de la pregunta.


1
¿Por qué const __dirname = path.resolve (); y no usar el __dirname incorporado?
TamusJRoyce

29

Por si acaso alguien interesado en la versión de una línea. :)

//or in typescript: import * as fs from 'fs';
const fs = require('fs');
!fs.existsSync(dir) && fs.mkdirSync(dir);

Presunto 1-liner en realidad no es 1 línea.
Desarrollador web híbrido

20

Simplemente puede usar mkdiry detectar el error si la carpeta existe.
Esto es asíncrono (así que las mejores prácticas) y seguro.

fs.mkdir('/path', err => { 
    if (err && err.code != 'EEXIST') throw 'up'
    .. safely do your stuff here  
    })

(Opcionalmente, agregue un segundo argumento con el modo).


Otros pensamientos:

  1. Podrías usar entonces o esperar usando promisify nativo .

    const util = require('util'), fs = require('fs');
    const mkdir = util.promisify(fs.mkdir);
    var myFunc = () => { ..do something.. } 
    
    mkdir('/path')
        .then(myFunc)
        .catch(err => { if (err.code != 'EEXIST') throw err; myFunc() })
  2. Puede hacer su propio método de promesa, algo como (no probado):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Para la verificación sincrónica, puede usar:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. O puedes usar una biblioteca, los dos seres más populares


1
para el enfoque prometedor n. ° 1, puede reorganizar la captura. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
Qué sería genial

Y usar en !==lugar de!=
Quentin Roy

18

Con el paquete fs-extra puede hacer esto con una sola línea :

const fs = require('fs-extra');

const dir = '/tmp/this/path/does/not/exist';
fs.ensureDirSync(dir);

¡Qué respuesta tan subestimada! fs-extra se ha convertido en algo imprescindible para mí. Creo que es una aberración escribir más de 10 líneas para verificar si existe una carpeta ...
538ROMEO

10

La mejor solución sería usar el módulo npm llamado node-fs-extra . Tiene un método llamado mkdirque crea el directorio que mencionaste. Si proporciona una ruta de directorio larga, creará las carpetas principales automáticamente. El módulo es un conjunto súper de módulo npm fs, por lo que puede usar todas las funciones fstambién si agrega este módulo.


6
var dir = 'path/to/dir';
try {
  fs.mkdirSync(dir);
} catch(e) {
  if (e.code != 'EEXIST') throw e;
}

44
Para Node.js v7.4.0, la documentación indica que fs.exists()está en desuso, pero fs.existsSync()no lo está. ¿Podría agregar un enlace a un recurso que diga que fs.existsSync()está depreciado?
francis

1
Las respuestas de solo código no son muy útiles para los usuarios que vengan a esta pregunta en el futuro. Edite su respuesta para explicar por qué su código resuelve el problema original
yivi


1
¡Gracias! Parece que la función existía en la versión 0.12, quedó en desuso en las versiones 4 y 5 y se restauró en la versión 6 y 7 ... Una especie de función zombi ...
francis

1
Sí, aparentemente NO está en desuso ahora a partir de Apr 2018: nodejs.org/api/fs.html#fs_fs_existssync_path
LeOn - Han Li

5
    var filessystem = require('fs');
    var dir = './path/subpath/';

    if (!filessystem.existsSync(dir)){
        filessystem.mkdirSync(dir);
    }else
    {
        console.log("Directory already exist");
    }

Esto puede ayudarte :)


5

ENOENT: no existe tal archivo o directorio

Solución

const fs = require('fs')  // in javascript
import * as fs from "fs"  // in typescript
import fs from "fs"       // in typescript

// it will create the directory if it does not exist.
!fs.existsSync(`./assets/`) && fs.mkdirSync(`./assets/`, { recursive: true })

1
esto está funcionando, gracias
Aljohn Yamaro

3

Me gustaría agregar un refactor de Promesa mecanografiada de la respuesta de josh3736 .

Hace lo mismo y tiene los mismos casos límite, simplemente utiliza Promesas, typedefs typedefs y funciona con "use estricto".

// https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation
const allRWEPermissions = parseInt("0777", 8);

function ensureFilePathExists(path: string, mask: number = allRWEPermissions): Promise<void> {
    return new Promise<void>(
        function(resolve: (value?: void | PromiseLike<void>) => void,
            reject: (reason?: any) => void): void{
            mkdir(path, mask, function(err: NodeJS.ErrnoException): void {
                if (err) {
                    if (err.code === "EEXIST") {
                        resolve(null); // ignore the error if the folder already exists
                    } else {
                        reject(err); // something else went wrong
                    }
                } else {
                    resolve(null); // successfully created folder
                }
            });
    });
}

3

Con Nodo 10 + ES6:

import path from 'path';
import fs from 'fs';

(async () => {
  const dir = path.join(__dirname, 'upload');

  try {
    await fs.promises.mkdir(dir);
  } catch (error) {
    if (error.code === 'EEXIST') {
      // Something already exists, but is it a file or directory?
      const lstat = await fs.promises.lstat(dir);

      if (!lstat.isDirectory()) {
        throw error;
      }
    } else {
      throw error;
    }
  }
})();

2

Puede usar el comando del sistema de archivos del nodo fs.stat para verificar si existe el directorio y fs.mkdir para crear un directorio con devolución de llamada, o fs.mkdirSync para crear un directorio sin devolución de llamada, como este ejemplo:

//first require fs
const fs = require('fs');

// Create directory if not exist (function)
const createDir = (path) => {
    // check if dir exist
    fs.stat(path, (err, stats) => {
        if (stats.isDirectory()) {
            // do nothing
        } else {
            // if the given path is not a directory, create a directory
            fs.mkdirSync(path);
        }
    });
};

1

Aquí hay una pequeña función para crear directorios recursivamente:

const createDir = (dir) => {
  // This will create a dir given a path such as './folder/subfolder' 
  const splitPath = dir.split('/');
  splitPath.reduce((path, subPath) => {
    let currentPath;
    if(subPath != '.'){
      currentPath = path + '/' + subPath;
      if (!fs.existsSync(currentPath)){
        fs.mkdirSync(currentPath);
      }
    }
    else{
      currentPath = subPath;
    }
    return currentPath
  }, '')
}

0

Usando async / await:

const mkdirP = async (directory) => {
  try {
    return await fs.mkdirAsync(directory);
  } catch (error) {
    if (error.code != 'EEXIST') {
      throw e;
    }
  }
};

Tendrá que prometer fs:

import nodeFs from 'fs';
import bluebird from 'bluebird';

const fs = bluebird.promisifyAll(nodeFs);
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.