Как записать файл, если родительской папки не существует?

94

Мне нужно записать файл по следующему пути:

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

Но '/folder1/folder2'пути может не существовать. Итак, я получаю следующую ошибку:

message = ENOENT, откройте /folder1/folder2/file.txt

Как я могу писать контент по этому пути?

Эрик
источник
2
fs.promises.mkdir(path.dirname('/folder1/folder2/file.txt'), {recursive: true}).then(x => fs.promises.writeFile('/folder1/folder2/file.txt', 'content'))
Offenso 05

Ответы:

128

Используйте mkdirp в сочетании с 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);
  });
}

Если весь путь уже существует, mkdirpнет. В противном случае он создаст для вас все недостающие каталоги.

Этот модуль делает то, что вы хотите: https://npmjs.org/package/writefile . Получил при поиске в Google "writefile mkdirp". Этот модуль возвращает обещание вместо того, чтобы принимать обратный вызов, поэтому не забудьте сначала прочитать введение в обещания. Это может действительно усложнить вам задачу.

Функция, которую я дал, работает в любом случае.

Мирн Стол
источник
Итак, если мы хотим дождаться его завершения, мы должны поместить все после него в обратный вызов? Есть ли другой способ?
Pete
@pete, если вы используете babel, вы можете использовать async / await примерно так: gist.github.com/lucasreppewelander/…
Лукас Реппе Веландер 08
11
Использовать fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
рекурсию
27

Я считаю, что самый простой способ сделать это - использовать метод outputFile () из модуля fs-extra .

Практически то же самое, что и writeFile (т.е. перезаписывает), за исключением того, что если родительский каталог не существует, он создается. параметры - это то, что вы передаете в fs.writeFile ().

Пример:

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

В наши дни он также имеет обещанную поддержку прямо из коробки !.

tkarls
источник
21

редактировать

Версия NodeJS 10.12.0добавила встроенную поддержку как для, так mkdirи mkdirSyncдля рекурсивного создания родительского директора со следующей recursive: trueопцией:

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

И если хотите fs Promises API, можете написать

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

Оригинальный ответ

Создавайте родительские каталоги рекурсивно, если они не существуют! ( Нулевые зависимости )

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

Применение

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

Демо

Попытайся!

Пояснения

  • [ОБНОВЛЕНИЕ] Это решение обрабатывает ошибки, связанные с платформой, например EISDIRдля Mac иEPERM иEACCES для Windows.
  • Это решение обрабатывает как относительные, так и абсолютные пути.
  • В случае относительных путей целевые каталоги будут созданы (разрешены) в текущем рабочем каталоге. Чтобы разрешить их относительно текущего каталога сценария, передайте{isRelativeToScript: true} .
  • Использование path.sepи path.resolve(), а не только/ конкатенация, чтобы избежать проблем с кросс-платформенностью.
  • Использование fs.mkdirSyncи обработка ошибки с помощью try/catchif для обработки условий гонки: другой процесс может добавить файл между вызовами fs.existsSync()иfs.mkdirSync() и исключение.
    • Другой способ добиться того, что можно было бы проверить , если файл существует , то его создания, т.е., if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. Но это антипаттерн, который делает код уязвимым для условий гонки.
  • Требуется Node v6 и новее для поддержки деструктуризации. (Если у вас возникли проблемы с реализацией этого решения со старыми версиями Node, просто оставьте мне комментарий)
Mouneer
источник
19

Возможно, наиболее просто, вы можете просто использовать модуль npm fs-path .

Тогда ваш код будет выглядеть так:

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');
  }
});
Kevincoleman
источник
3

Ты можешь использовать

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

statsэто fs.Statsтип объекта, можете проверить stats.isDirectory(). В зависимости от обследования errи statsумения что-то делать,fs.mkdir( ... ) или выдать ошибку.

Справка

Обновление: исправлены запятые в коде.

MikeD
источник
Значит, я не могу записать файл с помощью команды sibgle в nodejs?
Эрик
3

С node-fs-extra вы можете сделать это легко.

Установите это

npm install --save fs-extra

Затем используйте метод outputFile вместо 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!');
  }
})
Мухаммад Нуман
источник
2

Вот моя пользовательская функция для рекурсивного создания каталогов (без внешних зависимостей):

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);
math_lab3.ca
источник
2

Вот моя функция, которая работает в Node 10.12.0. Надеюсь, это поможет.

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!');
        });
    }
Кайлас
источник
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);
      });
    });
  }
);
Луат До
источник
Ответы, состоящие только из кода, считаются низкокачественными: обязательно объясните, что делает ваш код и как он решает проблему. Если вы добавите больше информации в свой пост, это поможет как спрашивающему, так и будущим читателям. См. « Объяснение полностью основанных на коде ответов»
Калос,
-1

Вот часть ответа Мирн Стол, выделенная как отдельный ответ:

Этот модуль делает то, что вы хотите: https://npmjs.org/package/writefile . Получил при поиске в Google "writefile mkdirp". Этот модуль возвращает обещание вместо того, чтобы принимать обратный вызов, поэтому не забудьте сначала прочитать введение в обещания. Это может действительно усложнить вам задачу.

Дэвид Браун
источник