Como escrever um arquivo se a pasta pai não existir?


93

Preciso gravar o arquivo no seguinte caminho:

fs.writeFile('/folder1/folder2/file.txt', 'content', function () {…});

Mas o '/folder1/folder2'caminho pode não existir. Portanto, recebo o seguinte erro:

mensagem = ENOENT, abra /folder1/folder2/file.txt

Como posso escrever conteúdo para esse caminho?


2
fs.promises.mkdir(path.dirname('/folder1/folder2/file.txt'), {recursive: true}).then(x => fs.promises.writeFile('/folder1/folder2/file.txt', 'content'))
Offenso

Respostas:


127

Use mkdirp em combinação com path.dirnamefirst.

var mkdirp = require('mkdirp');
var fs = require('fs');
var getDirName = require('path').dirname;

function writeFile(path, contents, cb) {
  mkdirp(getDirName(path), function (err) {
    if (err) return cb(err);

    fs.writeFile(path, contents, cb);
  });
}

Se todo o caminho já existe, mkdirpé um noop. Caso contrário, ele cria todos os diretórios ausentes para você.

Este módulo faz o que você deseja: https://npmjs.org/package/writefile . Peguei ao pesquisar por "writefile mkdirp" no Google. Este módulo retorna uma promessa em vez de receber um retorno de chamada, portanto, certifique-se de ler alguma introdução às promessas primeiro. Na verdade, pode complicar as coisas para você.

A função que dei funciona em qualquer caso.


Então, se quisermos esperar que ele seja concluído, temos que colocar tudo depois disso no retorno de chamada? Existe alguma outra maneira?
pete

@pete se você usar o babel, você poderia ir com async / await assim
Lucas Reppe Welander

11
Use recursivo:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

27

Acho que a maneira mais fácil de fazer isso é usar o método outputFile () do módulo fs-extra .

Quase o mesmo que writeFile (ou seja, sobrescreve), exceto que, se o diretório pai não existir, ele será criado. opções são o que você passaria para fs.writeFile ().

Exemplo:

var fs = require('fs-extra');
var file = '/tmp/this/path/does/not/exist/file.txt'

fs.outputFile(file, 'hello!', function (err) {
    console.log(err); // => null

    fs.readFile(file, 'utf8', function (err, data) {
        console.log(data); // => hello!
    });
});

Ele também promete suporte fora da caixa nos dias de hoje!


19

Talvez de forma mais simples, você pode apenas usar o módulo npm fs-path .

Seu código ficaria assim:

var fsPath = require('fs-path');

fsPath.writeFile('/folder1/folder2/file.txt', 'content', function(err){
  if(err) {
    throw err;
  } else {
    console.log('wrote a file like DaVinci drew machines');
  }
});

17

Editar

O NodeJS versão 10 adicionou um suporte nativo para ambos mkdire mkdirSyncpara criar o diretor pai recursivamente com recursive: trueas seguintes opções:

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

E se preferir fs Promises API, pode escrever

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

Resposta Original

Crie os diretórios pais recursivamente se eles não existirem! ( Zero dependências )

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

Demo

Tente!

Explicações

  • [UPDATE] erros Este específico da plataforma alças solução como EISDIRpara Mac e EPERMe EACCESpara Windows.
  • Esta solução trata de caminhos relativos e absolutos .
  • No caso de caminhos relativos, os diretórios de destino serão criados (resolvidos) no diretório de trabalho atual. Para resolvê-los em relação ao diretório de script atual, passe {isRelativeToScript: true}.
  • Usando path.sepe path.resolve(), não apenas /concatenação, para evitar problemas de plataforma cruzada.
  • Usando fs.mkdirSynce manipulando o erro com try/catchif lançado para lidar com condições de corrida: outro processo pode adicionar o arquivo entre as chamadas para fs.existsSync()e fs.mkdirSync()e causa uma exceção.
    • A outra maneira de conseguir isso seria verificar se um arquivo existe e, em seguida, criá-lo, ou seja if (!fs.existsSync(curDir) fs.mkdirSync(curDir);,. Mas esse é um antipadrão que deixa o código vulnerável a condições de corrida.
  • Requer o Node v6 e mais recente para oferecer suporte à desestruturação. (Se você tiver problemas para implementar esta solução com versões antigas do Node, deixe-me um comentário)

3

Você pode usar

fs.stat('/folder1/folder2', function(err, stats){ ... });

statsé um fs.Statstipo de objeto, você pode verificar stats.isDirectory(). Dependendo do exame erre statsvocê pode fazer algo, fs.mkdir( ... )ou lançar um erro.

Referência

Atualização: corrigidas as vírgulas no código.


Portanto, não consigo escrever o arquivo usando o comando sibgle no nodejs?
Erik

2

Esta é minha função personalizada para criar diretórios recursivamente (sem dependências externas):

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

var myMkdirSync = function(dir){
    if (fs.existsSync(dir)){
        return
    }

    try{
        fs.mkdirSync(dir)
    }catch(err){
        if(err.code == 'ENOENT'){
            myMkdirSync(path.dirname(dir)) //create parent dir
            myMkdirSync(dir) //create dir
        }
    }
}

myMkdirSync(path.dirname(filePath));
var file = fs.createWriteStream(filePath);

2

Aqui está minha função que funciona no Node 10.12.0. Espero que isso ajude.

const fs = require('fs');
function(dir,filename,content){
        fs.promises.mkdir(dir, { recursive: true }).catch(error => { console.error('caught exception : ', error.message); });
        fs.writeFile(dir+filename, content, function (err) {
            if (err) throw err;
            console.info('file saved!');
        });
    }

1

Com node-fs-extra, você pode fazer isso facilmente.

Instale-o

npm install --save fs-extra

Em seguida, use o método outputFile em vez de writeFileSync

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

fs.outputFile('tmp/test.txt', 'Hey there!', err => {
  if(err) {
    console.log(err);
  } else {
    console.log('The file was saved!');
  }
})

0

Aqui está parte da resposta de Myrne Stol dividida como uma resposta separada:

Este módulo faz o que você deseja: https://npmjs.org/package/writefile . Peguei ao pesquisar por "writefile mkdirp" no Google. Este módulo retorna uma promessa em vez de receber um retorno de chamada, portanto, certifique-se de ler alguma introdução às promessas primeiro. Na verdade, pode complicar as coisas para você.


0
let name = "./new_folder/" + file_name + ".png";
await driver.takeScreenshot().then(
  function(image, err) {
    require('mkdirp')(require('path').dirname(name), (err) => {
      require('fs').writeFile(name, image, 'base64', function(err) {
        console.log(err);
      });
    });
  }
);

Respostas somente de código são consideradas de baixa qualidade: certifique-se de fornecer uma explicação sobre o que seu código faz e como ele resolve o problema. Ajudará tanto o autor da pergunta quanto os futuros leitores se você puder adicionar mais informações em sua postagem. Consulte Explicando respostas inteiramente baseadas em código
Calos,
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.