Як написати файл, якщо батьківська папка не існує?


93

Мені потрібно написати файл за таким шляхом:

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

Відповіді:


127

Використовуйте 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це noop. В іншому випадку він створює для вас усі відсутні каталоги.

Цей модуль робить те, що ви хочете: https://npmjs.org/package/writefile . Зрозумів, коли погуглив "writefile mkdirp". Цей модуль повертає обіцянку, замість того, щоб приймати зворотний виклик, тому спочатку прочитайте деякі вступні дані до обіцянок. Це насправді може ускладнити для вас щось.

Функція, яку я дав, працює в будь-якому випадку.


Отже, якщо ми хочемо почекати, поки він завершиться, ми повинні помістити все після нього у зворотний виклик? Чи є якийсь інший шлях?
Піт,

@pete, якщо ти використовуєш babel, ти можеш піти з async / await, як цей суть: gist.github.com/lucasreppewelander/…
Лукас Реппе Веландер

11
Використовуйте рекурсивно:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

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

У наші дні він також має підтримку обіцянки !.


19

Можливо, найпростіше, ви можете просто використовувати модуль fs-path npm.

Тоді ваш код виглядатиме так:

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

18

Редагувати

Версія 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');

Демо

Спробуй це!

Пояснення

  • [UPDATE] Це ручки рішення платформи специфічні помилки , як 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, просто залиште мені коментар)

3

Можна використовувати

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

statsце fs.Statsтип об’єкта, ви можете перевірити stats.isDirectory(). Залежно від обстеження errі statsви можете щось зробити, fs.mkdir( ... )або викинути помилку.

Довідково

Оновлення: виправлені коми в коді.


Тож я не можу написати файл за допомогою команди sibgle у nodejs?
Ерік

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

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

2

За допомогою 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!');
  }
})

0

Ось частина відповіді Мірн Стол розбита як окрема відповідь:

Цей модуль робить те, що ви хочете: https://npmjs.org/package/writefile . Зрозумів, коли погуглив "writefile mkdirp". Цей модуль повертає обіцянку замість того, щоб приймати зворотний дзвінок, тому спочатку обов’язково прочитайте вступ до обіцянок. Це насправді може ускладнити для вас щось.


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

Відповіді лише для коду вважаються низькою якістю: обов’язково надайте пояснення, що робить ваш код і як він вирішує проблему. Це допоможе автору запиту та майбутнім читачам, якщо ви зможете додати більше інформації у своєму дописі. Див. Пояснення повністю заснованих на коді відповідей
Калос,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.