Node.js crea una carpeta o usa una existente

186

Ya he leído la documentación de Node.js y, a menos que me haya perdido algo, no dice qué contienen los parámetros en ciertas operaciones, en particular fs.mkdir(). Como puede ver en la documentación, no es mucho.

Actualmente, tengo este código, que intenta crear una carpeta o usar una existente en su lugar:

fs.mkdir(path,function(e){
    if(!e || (e && e.code === 'EEXIST')){
        //do something with contents
    } else {
        //debug
        console.log(e);
    }
});

Pero me pregunto si esta es la forma correcta de hacerlo. ¿Verificando el código es EEXISTla forma correcta de saber que la carpeta ya existe? Sé que puedo hacer fs.stat()antes de hacer el directorio, pero eso ya serían dos visitas al sistema de archivos.

En segundo lugar, ¿hay una documentación completa o al menos más detallada de Node.js que contenga detalles sobre qué contienen los objetos de error, qué parámetros significan, etc.

Joseph
fuente
31
Pequeño pico, pero deshazte del e &&. Si !efalla, entonces sabes que ees verdad.
Odio a los perezosos el

Respuestas:

236

Una buena forma de hacerlo es usar el módulo mkdirp .

$ npm install mkdirp

Úselo para ejecutar la función que requiere el directorio. La devolución de llamada se llama después de crear la ruta o si la ruta ya existía. El error errse establece si mkdirp no pudo crear la ruta del directorio.

var mkdirp = require('mkdirp');
mkdirp('/tmp/some/path/foo', function(err) { 

    // path exists unless there was an error

});
Teemu Ikonen
fuente
3
Me parece que la respuesta correcta (léase 'sin dependencia agregada') sería la de abajo, por @Raugaral, usando fs.exists(Sync).
Ricardo Pedroni
@meawoppl, es 'makedirectory'p. La 'p' es desconocida.
Andrew
44
@RicardoPedroni La forma correcta es usar un módulo. Los módulos suelen tratar de resolver un problema de todo corazón y, a menudo, se mantienen. Puede actualizarlos fácilmente con npm. Además, debe evitar específicamente el uso de fs.exists [Sync] ya que su uso implica condiciones de carrera.
1j01
16
@ 1j01 No creo que la forma correcta sea usar un módulo si la plataforma admite de forma nativa la operación. Ese es un camino al caos. Tengo que aceptar que hay mejores respuestas desde un punto de vista técnico.
c ..
2
@ 1j01 Además, el uso de operaciones de sincronización implica condiciones de carrera porque su uso es una resolución para ellos.
c ..
193

Editar: debido a que esta respuesta es muy popular, la actualicé para reflejar las prácticas actualizadas.

Nodo> = 10

La nueva { recursive: true }opción de Node's fsahora permite esto de forma nativa. Esta opción imita el comportamiento de UNIX mkdir -p. Se asegurará recursivamente de que exista cada parte de la ruta y no arrojará un error si alguno de ellos lo hace.

(Nota: aún podría arrojar errores como EPERMo EACCESS, por lo que es mejor envolverlo en un try {} catch (e) {}si su implementación es susceptible a ello).

Versión sincrónica.

fs.mkdirSync(dirpath, { recursive: true })

Versión asíncrona

await fs.promises.mkdir(dirpath, { recursive: true })

Versiones de nodos anteriores

Usando a try {} catch (err) {}, puedes lograr esto con mucha gracia sin encontrar una condición de carrera.

Para evitar el tiempo muerto entre verificar la existencia y crear el directorio, simplemente tratamos de crearlo directamente, y no tenemos en cuenta el error si es EEXIST (el directorio ya existe).

EEXISTSin embargo, si el error no es así , debemos arrojar un error, porque podríamos estar tratando con algo como un EPERMoEACCES

function ensureDirSync (dirpath) {
  try {
    return fs.mkdirSync(dirpath)
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }
}

Por mkdir -p-como el comportamiento recursivo, por ejemplo ./a/b/c, habría que llamarlo en cada parte de la dirpath, por ejemplo ./a, ./a/b,.a/b/c

Christophe Marois
fuente
var fs = Npm.require ('fs'); var dir = process.env.PWD + '/ files / users /' + this.userId + '/'; intente {fs.mkdirSync (dir); } catch (e) {if (e.code! = 'EEXIST') lanza e; }
Aaron
Probé su código, cree un script js que use la creación del catálogo de esta manera: mkdirpSync (path.join (__ dirname, 'first', 'second', 'third', 'ololol', 'works')); Pero obtuve este error: $ node 1.js fs.js: 747 return encuadernando.mkdir (pathModule._makeLong (ruta), ^ Error: EPERM, operación no permitida 'C: \' en Error (nativo) en Object.fs. mkdirSync (fs.js: 747: 18) en mkdirpSync (C: \ Users \ MAXIM \ Desktop \ test \ 1.js: 15: 8) en Object. <anónimo> (C: \ Users \ MAXIM \ Desktop \ test \ 1.js: 19: 1) ... ¿Podría sugerir qué podría estar mal? Utilizado en Windows obviamente :)
Alendorff
EPERM parece ser un problema de permiso, por lo que el guión no habría interrumpido la ejecución de todos modos
Christophe Marois
Creo que sería mejor: var mkdirpSync = function (dirpath) {var parts = dirpath.split (path.sep); for (var i = 1; i <= parts.length; i ++) {try {fs.mkdirSync (path.join.apply (null, parts.slice (0, i))); } catch (error) {if (error.code! = 'EEXIST') {error de lanzamiento; }}}}
Manish
1
advertencia: no funciona si su ruta comienza con un /
acemtp
62

Si desea un revestimiento rápido y sucio, use esto:

fs.existsSync("directory") || fs.mkdirSync("directory");
marekventur
fuente
1
fs.existsestá en desuso: nodejs.org/api/fs.html#fs_fs_exists_path_callback
adius
77
Sin embargo, fs.existsSync (...) no está en desuso, por lo que esta respuesta parece estar bien.
Dan Haywood el
¡Aviso! No funciona para "dir / foo / bar", es decir, carece de la función de bandera mkdir -p
Karl Pokus
Esto también tiene una condición de carrera
Evert
26

Los documentos de node.js fs.mkdirbásicamente difieren de la página de manual de Linux paramkdir(2) . Eso indica que EEXISTtambién se indicará si la ruta existe pero no es un directorio que crea un caso de esquina incómodo si sigue esta ruta.

Es mejor que llames fs.stat que que le dirá si la ruta existe y si es un directorio en una sola llamada. Para (lo que supongo es) el caso normal donde el directorio ya existe, es solo un hit del sistema de archivos.

Estos fsmétodos de módulo son envoltorios delgados alrededor de las API nativas de C, por lo que debe verificar las páginas de manual a las que se hace referencia en los documentos de node.js para obtener detalles.

JohnnyHK
fuente
19
Llamar statantes mkdirtiene el potencial de una condición de carrera, tenga esto en cuenta.
Roger Lipscombe
24

Puedes usar esto:

if(!fs.existsSync("directory")){
    fs.mkdirSync("directory", 0766, function(err){
        if(err){
            console.log(err);
            // echo the result back
            response.send("ERROR! Can't make the directory! \n");
        }
    });
}
Raugaral
fuente
1
-1. No creo que esto funcione, statSyncarrojará un error si la entidad no existe en absoluto, bloqueando el código. Necesita envolver esto en un try/catchbloque.
Chris Foster
2
Lo siento estoy equivocado. cambiar "statSync" por "existSync"
Raugaral
55
Según nodejs.org/api/fs.html#fs_fs_mkdirsync_path_mode, la variante de sincronización de mkdir no acepta una devolución de llamada
danwellman
1
De acuerdo con nodejs.org/api/fs.html#fs_fs_existssync_path , fs.existsSync()y fs.exists()quedará en desuso.
pau.moreno
7

Propongo una solución sin módulos (nunca se recomienda acumular módulos para su mantenimiento, especialmente para funciones pequeñas que se pueden escribir en unas pocas líneas ...):

ÚLTIMA ACTUALIZACIÓN :

En v10.12.0, NodeJS implementa opciones recursivas:

// Create recursive folder
fs.mkdir('my/new/folder/create', { recursive: true }, (err) => { if (err) throw err; });

ACTUALIZACIÓN

// Get modules node
const fs   = require('fs');
const path = require('path');

// Create 
function mkdirpath(dirPath)
{
    if(!fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK))
    {
        try
        {
            fs.mkdirSync(dirPath);
        }
        catch(e)
        {
            mkdirpath(path.dirname(dirPath));
            mkdirpath(dirPath);
        }
    }
}

// Create folder path
mkdirpath('my/new/folder/create');
Liberateur
fuente
fs.exists()está en desuso en el nodo v9. utilizar fs.access()en su lugar. (regresa undefinedsi el archivo existe; de ​​lo contrario arroja un error ENOENT)
chharvey
Sin ningún paquete npm, está funcionando. Es un código valioso. Gracias
Karthik Sridharan
Este es bastante mejor para crear una carpeta en la ruta larga existente;) Gracias, hombre.
Tsung Goh
1
¿Qué hay de fs.mkdirSync('my/new/folder/create', {recursive: true})?
saitho
Gracias ! Actualizo mi publicación para ayudar a otros. El nodo 10.12.0 era demasiado reciente.
Liberateur
4

También puedes usar fs-extra , que proporciona muchas operaciones de archivos de uso frecuente.

Código de muestra:

var fs = require('fs-extra')

fs.mkdirs('/tmp/some/long/path/that/prob/doesnt/exist', function (err) {
  if (err) return console.error(err)
  console.log("success!")
})

fs.mkdirsSync('/tmp/another/path')

documentos aquí: https://github.com/jprichardson/node-fs-extra#mkdirsdir-callback

Geng Jiawen
fuente
4

Aquí está el código ES6 que uso para crear un directorio (cuando no existe):

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

function createDirectory(directoryPath) {
  const directory = path.normalize(directoryPath);

  return new Promise((resolve, reject) => {
    fs.stat(directory, (error) => {
      if (error) {
        if (error.code === 'ENOENT') {
          fs.mkdir(directory, (error) => {
            if (error) {
              reject(error);
            } else {
              resolve(directory);
            }
          });
        } else {
          reject(error);
        }
      } else {
        resolve(directory);
      }
    });
  });
}

const directoryPath = `${__dirname}/test`;

createDirectory(directoryPath).then((path) => {
  console.log(`Successfully created directory: '${path}'`);
}).catch((error) => {
  console.log(`Problem creating directory: ${error.message}`)
});

Nota:

  • Al comienzo de la createDirectoryfunción, normalizo la ruta para garantizar que el tipo de separador de ruta del sistema operativo se utilizará de forma coherente (por ejemplo, esto se convertirá C:\directory/testenC:\directory\test (cuando esté en Windows)
  • fs.existsestá en desuso , es por eso que uso fs.statpara verificar si el directorio ya existe
  • Si no existe un directorio, el código de error será ENOENT( E rror NO ENT ry)
  • El directorio en sí será creado usando fs.mkdir
  • Prefiero la función asincrónica fs.mkdirsobre su contraparte de bloqueo fs.mkdirSyncy, debido al ajuste Promise, se garantizará que la ruta del directorio solo se devolverá después de que el directorio se haya creado correctamente
Benny Neugebauer
fuente
Gracias por una solución limpia que no involucra módulos innecesarios. Funcionó perfectamente para mí. ¡Ojalá hubiera más respuestas como esta!
Ken Lyon
3

En mi opinión, es mejor que no cuentes los hits del sistema de archivos mientras codificas en Javascript. Sin embargo, (1) staty mkdiry (2) mkdiry el cheque (o descarte) el código de error, ambas formas son formas adecuadas para hacer lo que quiere.

Chul-Woong Yang
fuente
-1, no veo cómo verificar o descartar podrían ser formas correctas de hacerlo. Esto es prácticamente una no respuesta.
Matt Ball
Es una buena manera de crear un directorio o usar uno existente. No veo por qué no lo ves. Verificar el código de error es cortésmente bueno, mientras que descartar el código de error es solo uno bueno. no estas de acuerdo
Chul-Woong Yang
1
Tal vez este es un problema de barrera del idioma, pero leí esto simplemente como no respondiendo a la pregunta que hace el OP.
Matt Ball
Ya veo cuál es tu punto. Sin embargo, creo que hay muchas maneras de hacer las cosas bien. Gracias.
Chul-Woong Yang
2

cree un directorio de nombres dinámico para cada usuario ... use este código

***suppose email contain user mail address***

var filessystem = require('fs');
var dir = './public/uploads/'+email;

if (!filessystem.existsSync(dir)){
  filessystem.mkdirSync(dir);

}else
{
    console.log("Directory already exist");
}
Adiii
fuente
1

Puede hacer todo esto con el módulo Sistema de archivos.

const
  fs = require('fs'),
  dirPath = `path/to/dir`

// Check if directory exists.
fs.access(dirPath, fs.constants.F_OK, (err)=>{
  if (err){
    // Create directory if directory does not exist.
    fs.mkdir(dirPath, {recursive:true}, (err)=>{
      if (err) console.log(`Error creating directory: ${err}`)
      else console.log('Directory created successfully.')
    })
  }
  // Directory now exists.
})

Realmente ni siquiera necesita verificar si el directorio existe. El siguiente código también garantiza que el directorio ya existe o está creado.

const
  fs = require('fs'),
  dirPath = `path/to/dir`

// Create directory if directory does not exist.
fs.mkdir(dirPath, {recursive:true}, (err)=>{
  if (err) console.log(`Error creating directory: ${err}`)
  // Directory now exists.
})
chico mayor
fuente
0

La respuesta de Raugaral pero con la funcionalidad -p. Feo, pero funciona:

function mkdirp(dir) {
    let dirs = dir.split(/\\/).filter(asdf => !asdf.match(/^\s*$/))
    let fullpath = ''

    // Production directory will begin \\, test is on my local drive.
    if (dirs[0].match(/C:/i)) {
        fullpath = dirs[0] + '\\'
    }
    else {
        fullpath = '\\\\' + dirs[0] + '\\'
    }

    // Start from root directory + 1, build out one level at a time.
    dirs.slice(1).map(asdf => {
        fullpath += asdf + '\\'
        if (!fs.existsSync(fullpath)) {
            fs.mkdirSync(fullpath)
        }
    })
}//mkdirp
usuario8675309
fuente
0

Como una nueva alternativa a la respuesta de Teemu Ikonen , que es muy simple y fácil de leer, es usar el ensureDirmétodo del fs-extrapaquete.

No solo se puede usar como un reemplazo descarado para el fsmódulo integrado , sino que también tiene muchas otras funcionalidades además de las funcionalidades del fspaquete.

El ensureDirmétodo, como su nombre indica, asegura que el directorio exista. Si la estructura del directorio no existe, se crea. Al igual mkdir -p. No solo la carpeta final, sino que se crea la ruta completa si aún no existe.

el que se proporciona arriba es la asyncversión del mismo. También tiene un método sincrónico para realizar esto en la forma del ensureDirSyncmétodo.

Rai
fuente
0

La respuesta de @ Liberateur anterior no funcionó para mí (Nodo v8.10.0). Una pequeña modificación hizo el truco, pero no estoy seguro de si esta es una forma correcta. Por favor recomiende.

// Get modules node
const fs   = require('fs');
const path = require('path');

// Create
function mkdirpath(dirPath)
{
    try {
        fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
    }
    catch(err) {
        try
        {
            fs.mkdirSync(dirPath);
        }
        catch(e)
        {
            mkdirpath(path.dirname(dirPath));
            mkdirpath(dirPath);
        }
    }
}

// Create folder path
mkdirpath('my/new/folder/create');
nik
fuente