Como criar um diretório se ele não existir usando o Node.js?

656

É este o caminho certo para criar um diretório, se ele não existir. Deve ter permissão total para o script e legível por outras pessoas.

var dir = __dirname + '/upload';
if (!path.existsSync(dir)) {
    fs.mkdirSync(dir, 0744);
}
Whisher
fonte
1
Possível duplicado de Node.js criar a pasta ou o uso existente
Benny Neugebauer

Respostas:

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

if (!fs.existsSync(dir)){
    fs.mkdirSync(dir);
}
chovy
fonte
28
Se você estiver executando esta operação na inicialização ou inicialização do aplicativo, não há problema em bloquear a execução, pois você faria a mesma coisa se o fizesse de forma assíncrona. Se você estiver criando um diretório como uma operação recorrente, é uma prática ruim, mas provavelmente não causará nenhum problema de desempenho, mas é um hábito ruim, no entanto. Use apenas para inicializar seu aplicativo ou para operações únicas.
tsturzl 14/08/2015
20
existsSync () não é obsoleto, existe () é embora - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick
usando * Syncmétodos geralmente é um não-não: não queremos bloquear o ciclo de eventos
Max Heiber
14
Usar métodos de sincronização é bom para scripts locais e, obviamente, não é uma boa idéia para um servidor.
Pier
Se esse bloco é cercado por setTimeout, é assíncrona .....................
Bryan Graça
185

Não, por várias razões.

  1. O pathmódulo não possui um método exists/ existsSync. Está no fsmódulo. (Talvez você tenha cometido um erro de digitação na sua pergunta?)

  2. Os documentos explicitamente o desencorajam de usar exists.

    fs.exists()é um anacronismo e existe apenas por razões históricas. Quase nunca deve haver um motivo para usá-lo em seu próprio código.

    Em particular, verificar se um arquivo existe antes de abri-lo é um antipadrão que o deixa vulnerável às condições de corrida: outro processo pode remover o arquivo entre as chamadas para fs.exists()e fs.open(). Basta abrir o arquivo e manipular o erro quando ele não estiver lá.

    Como estamos falando de um diretório em vez de um arquivo, esse conselho implica que você deve ligar mkdire ignorar incondicionalmente EEXIST.

  3. Em geral, você deve evitar os Syncmétodos * . Eles estão bloqueando, o que significa que absolutamente nada no seu programa pode acontecer enquanto você acessa o disco. Essa é uma operação muito cara e o tempo que leva para quebrar a suposição principal do loop de eventos do nó.

    Os Syncmétodos * geralmente são bons em scripts rápidos de uso único (aqueles que fazem uma coisa e depois saem), mas quase nunca devem ser usados ​​quando você está gravando um servidor: seu servidor não poderá responder a ninguém por todo o período dos pedidos de E / S. Se várias solicitações de clientes exigirem operações de E / S, o servidor será interrompido muito rapidamente.


    A única vez em que eu consideraria o uso de Syncmétodos * em um aplicativo de servidor é em uma operação que acontece uma vez (e apenas uma vez), na inicialização. Por exemplo, require na verdadereadFileSync , usa para carregar módulos.

    Mesmo assim, você ainda precisa ter cuidado, pois muitas E / S síncronas podem diminuir desnecessariamente o tempo de inicialização do servidor.


    Em vez disso, você deve usar os métodos de E / S assíncrona.

Portanto, se juntarmos esses conselhos, teremos algo assim:

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

E podemos usá-lo assim:

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

Obviamente, isso não leva em conta casos extremos como

  • O que acontece se a pasta for excluída enquanto o programa estiver em execução? (supondo que você verifique apenas se existe uma vez durante a inicialização)
  • O que acontece se a pasta já existir, mas com as permissões erradas?
josh3736
fonte
1
existe uma maneira de evitar SyntaxError: literais octais não são permitidos no modo estrito?
Whisher
8
Escreva como decimal. 0744 == 484.
precisa saber é o seguinte
3
Uma alternativa é usar um módulo que estende o fs para ter essa funcionalidade, como github.com/jprichardson/node-fs-extra
Bret
essa bandeira de "máscara" ainda é relevante em 2019? qual era o objetivo disso?
Oldboy
É o modo de arquivo unix - as permissões de leitura / gravação do diretório.
precisa saber é o seguinte
44

Eu encontrei e módulo npm que funciona como um encanto para isso. É simplesmente fazer um mkdir recursivamente quando necessário, como um "mkdir -p".

https://www.npmjs.com/package/mkdirp

Toni Gamez
fonte
33

O mkdirmétodo tem a capacidade de criar recursivamente todos os diretórios em um caminho que não existe e ignorar os que existem.

Nos documentos do nó v10 / 11 :

// 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: Você precisará importar o fsmódulo interno primeiro.

Agora, aqui está um exemplo um pouco mais robusto que aproveita os Módulos ES nativos (com sinalizador ativado e extensão .mjs), manipula caminhos não raiz e responde por nomes de caminho completos:

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

Você pode usá-lo como createDirectories('/components/widget/widget.js');.

E, é claro, você provavelmente desejaria ter mais fantasia usando promessas com async / waitit para alavancar a criação de arquivos de uma maneira síncrona mais legível quando os diretórios forem criados; mas isso está além do escopo da pergunta.

um pouco menos
fonte
1
Por que const __dirname = path.resolve (); e não usar o __dirname interno?
TamusJRoyce 24/02
29

Apenas no caso de alguém interessado na versão de uma linha. :)

//or in typescript: import * as fs from 'fs';
const fs = require('fs');
!fs.existsSync(dir) && fs.mkdirSync(dir);
LeOn - Han Li
fonte
1-liner alegado, na verdade não 1 linha.
Hybrid web dev
20

Você pode simplesmente usar mkdire capturar o erro se a pasta existir.
Isso é assíncrono (portanto, é uma prática recomendada) e seguro.

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

(Opcionalmente, adicione um segundo argumento com o modo.)


Outros pensamentos:

  1. Você pode usar ou aguardar usando o 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. Você pode criar seu próprio método de promessa, algo como (não testado):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Para verificação síncrona, você pode usar:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. Ou você pode usar uma biblioteca, as duas mais populares

    • mkdirp (apenas pastas)
    • fsextra (superconfigura fs, adiciona muitas coisas úteis)
SamGoody
fonte
1
para a abordagem promissora nº 1, você pode reorganizar a captura. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
O que seria legal
E use em !==vez de!=
Quentin Roy
18

Com o pacote fs-extra , você pode fazer isso com uma linha :

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

const dir = '/tmp/this/path/does/not/exist';
fs.ensureDirSync(dir);
Galki
fonte
Uma resposta tão subestimada! FS-extra tem bacame um deve ter para mim. Eu acho que é uma aberração escrever 10+ linhas apenas para verificar se existe uma pasta ...
538ROMEO
10

A melhor solução seria usar o módulo npm chamado node-fs-extra . Ele possui um método chamado mkdirque cria o diretório que você mencionou. Se você especificar um caminho de diretório longo, ele criará as pastas pai automaticamente. O módulo é um superconjunto de módulo npm fs, portanto, você pode usar todas as funções fstambém se adicionar este módulo.

Abdul Vajid
fonte
6
var dir = 'path/to/dir';
try {
  fs.mkdirSync(dir);
} catch(e) {
  if (e.code != 'EEXIST') throw e;
}
Ping.Goblue
fonte
4
Para o Node.js. v7.4.0, a documentação declara fs.exists()reprovado, mas fs.existsSync()não é. Você poderia adicionar um link a um recurso dizendo que fs.existsSync()está depreciado?
francis
1
As respostas somente de código não são muito úteis para os usuários que chegarem a essa pergunta no futuro. Edite sua resposta para explicar por que seu código resolve o problema original
yivi
3
@francis, hmm, eu estava olhando para o Node.js v5, nodejs.org/docs/latest-v5.x/api/fs.html#fs_fs_existssync_path
Ping.Goblue
1
Obrigado! Parece que a função existia na versão 0.12, ficou obsoleta na versão 4 e 5 e foi restaurado na versão 6 e 7 ... Kind of a função zombi ...
francis
1
Sim, aparentemente ele é NÃO obsoleta agora como de Apr 2018: nodejs.org/api/fs.html#fs_fs_existssync_path
Leão - Han Li
5
    var filessystem = require('fs');
    var dir = './path/subpath/';

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

Isso pode ajudá-lo :)

Vishnu S Babu
fonte
5

ENOENT: esse arquivo ou diretório não existe

Solução

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 })
WasiF
fonte
1
isso está funcionando, obrigado
Aljohn Yamaro
3

Gostaria de adicionar um refatorador de promessa de texto datilografado da resposta de josh3736 .

Ele faz a mesma coisa e tem os mesmos casos extremos, por acaso usa Promessas, typedefs datilografados e trabalha com "use strict".

// 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
                }
            });
    });
}
Nathan Cooper
fonte
3

Com o nó 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;
    }
  }
})();
sdgfsdh
fonte
2

Você pode usar o comando Sistema de Arquivos dofs.stat para verificar se dir existe e fs.mkdir para criar um diretório com retorno de chamada ou fs.mkdirSync para criar um diretório sem retorno de chamada, como neste exemplo:

//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);
        }
    });
};
majid jiji
fonte
1

Aqui está uma pequena função para criar diretórios 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
  }, '')
}
MrBlenny
fonte
0

Usando assíncrono / espera:

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

Você precisará promisificar fs:

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

const fs = bluebird.promisifyAll(nodeFs);
sdgfsdh
fonte