Створення каталогу під час запису у файл у Node.js


134

Я займався Node.js і виявив невелику проблему. У мене є сценарій, який знаходиться в каталозі під назвою data. Я хочу, щоб сценарій записував деякі дані у файл у підкаталозі всередині dataпідкаталогу. Однак я отримую таку помилку:

{ [Error: ENOENT, open 'D:\data\tmp\test.txt'] errno: 34, code: 'ENOENT', path: 'D:\\data\\tmp\\test.txt' }

Код такий:

var fs = require('fs');
fs.writeFile("tmp/test.txt", "Hey there!", function(err) {
    if(err) {
        console.log(err);
    } else {
        console.log("The file was saved!");
    }
}); 

Чи може хтось допомогти мені дізнатися, як змусити Node.js створити структуру каталогів, якщо вона не виходить для запису у файл?


1
fs.promises.mkdir(path.dirname("tmp/test.txt"), {recursive: true}).then(x => fs.promises.writeFile("tmp/test.txt", "Hey there!"))
Оффенсо

Відповіді:


127

Вузол> 10.12.0

fs.mkdir тепер приймає такий { recursive: true }варіант:

// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});

або з обіцянкою:

fs.promises.mkdir('/tmp/a/apple', { recursive: true }).catch(console.error);

Вузол <= 10.11.0

Ви можете вирішити це за допомогою пакету на зразок mkdirp або fs-extra . Якщо ви не хочете встановлювати пакет, див. Відповідь Тіаго Перес Франса нижче.


4
Це той, з ким я йду ... ці статистичні дані перемогли мене.
Аран Малхолланд

зауважте, що fs.promisesвсе ще експериментальний nodejs.org/dist/latest-v10.x/docs/api/…
lasec0203

132

Якщо ви не хочете використовувати будь-який додатковий пакет, перед тим, як створити файл, ви можете зателефонувати за допомогою наступної функції:

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

function ensureDirectoryExistence(filePath) {
  var dirname = path.dirname(filePath);
  if (fs.existsSync(dirname)) {
    return true;
  }
  ensureDirectoryExistence(dirname);
  fs.mkdirSync(dirname);
}

2
Це слід використовувати statSyncзамість existsSync, на основі stackoverflow.com/questions/4482686 / ...
GavinR

1
pathтакож пакет, який потрібно вимагати так само fs: var path = require('path')на випадок, коли хтось цікавиться. Дивіться документацію на вузол .
Рафаель Емшофф


6
Виникла певна плутанина щодо того, була функція fs.existsSync застарілою чи ні. Спочатку, на моє розуміння, я подумав, що це було, тому я оновив відповідь, щоб це відобразити. Але зараз, як вказує @zzzzBov, у документації чітко зазначено, що лише fs.exists застаріло, використання fs.existsSync все ще діє. З цієї причини я видалив попередній код, і моя відповідь тепер містить лише більш просте рішення (з використанням fs.existsSync).
Tiago Peres França

1
@chrismarx уявіть собі такий шлях: "/home/documents/a/b/c/myfile.txt". "/ додому / документи" існує, поки все перед цим немає. Коли "secureDirectoryExistence" вперше викликається, ім'я dirname - "/ home / документи / a / b / c". Я не можу зараз зателефонувати fs.mkdirSync (dirname), оскільки "/ home / документи / a / b" також не існує. Щоб створити каталог "c", мені потрібно спершу переконатися у існуванні "/ home / dokuments / a / b".
Tiago Peres França

43

За допомогою node-fs-extra ви можете це легко зробити.

Встановіть його

npm install --save fs-extra

Потім використовуйте outputFileметод. У його документації написано:

Майже те саме, що і writeFile (тобто він перезаписується), за винятком того, що якщо батьківський каталог не існує, він створюється.

Ви можете використовувати його трьома способами:

Стиль зворотного дзвінка

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

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

Використання обіцянок

Якщо ви використовуєте обіцянки , і я сподіваюся, що так, це код:

fse.outputFile('tmp/test.txt', 'Hey there!')
   .then(() => {
       console.log('The file was saved!');
   })
   .catch(err => {
       console.error(err)
   });

Синхронізувати версію

Якщо ви хочете версію синхронізації, просто використовуйте цей код:

fse.outputFileSync('tmp/test.txt', 'Hey there!')

Для повної довідки перегляньте outputFileдокументацію та всі підтримувані методи node-fs .


26

Безсоромне оповіщення проб!

Вам доведеться перевірити кожен каталог у потрібній структурі шляху та створити його вручну, якщо він не існує. Усі інструменти для цього вже є у модулі fs Node, але все це можна зробити просто за допомогою мого модуля mkpath: https://github.com/jrajav/mkpath


1
це створить файл безпосередньо або просто структуру каталогу? Я шукаю рішення, яке створить файл разом зі структурою каталогу при створенні файлу.
Гірвеш

Просто структура каталогу. Ви спочатку будете mkdir / path, і якщо помилок не було, приступайте до написання файлу. Буде досить просто, щоб записати функцію, щоб робити обидві одночасно, даючи повний шлях до файлу для запису - просто розділіть ім'я файлу за допомогою path.basename
jrajav

1
Насправді це було так просто, що я написав це за 2 хвилини . :) (Неперевірено)
jrajav

Оновлення: випробувано та відредаговано, будь ласка, спробуйте ще раз, якщо це не спрацювало з першого разу.
jrajav

8
@Kiyura Чим це відрізняється від широко використовуваного mkdirp ?
Девід Уелдон

9

Оскільки я поки не можу коментувати, я публікую розширену відповідь на основі фантастичного рішення @ tiago-peres-frança (спасибі!). Його код не складає каталог у випадку, коли в шляху відсутня лише остання директорія, наприклад, вхід "C: / test / abc" і "C: / test" вже існує. Ось фрагмент, який працює:

function mkdirp(filepath) {
    var dirname = path.dirname(filepath);

    if (!fs.existsSync(dirname)) {
        mkdirp(dirname);
    }

    fs.mkdirSync(filepath);
}

1
Це тому, що рішення @ tiago очікує шлях до файлу . У вашому випадку abcінтерпретується як файл, для якого потрібно створити каталог. Щоб також створити abcкаталог, додайте у свій шлях фіктивний файл, наприклад C:/test/abc/dummy.txt.
Sphinxxx

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

1
@Offenso - це найкраще рішення, але лише для Node.js версії 10.12 і вище.
Нікенсол

8

Моя порада: намагайтеся не покладатися на залежності, коли ви можете це легко зробити за допомогою декількох рядків кодів

Ось що ви намагаєтеся досягти у 14 рядках коду:

fs.isDir = function(dpath) {
    try {
        return fs.lstatSync(dpath).isDirectory();
    } catch(e) {
        return false;
    }
};
fs.mkdirp = function(dirname) {
    dirname = path.normalize(dirname).split(path.sep);
    dirname.forEach((sdir,index)=>{
        var pathInQuestion = dirname.slice(0,index+1).join(path.sep);
        if((!fs.isDir(pathInQuestion)) && pathInQuestion) fs.mkdirSync(pathInQuestion);
    });
};

1
Хіба третій рядок не був би кращим таким? return fs.lstatSync(dpath).isDirectory(), інакше що буде, якщо isDirectory () поверне помилкове значення?
Джорджіо Аресу

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

1
@Offenso це не підтримується вузлом 8
Ievgen Найда

2

Я щойно опублікував цей модуль, бо мені потрібна ця функціональність.

https://www.npmjs.org/package/filendir

Він працює як обгортка навколо методів Node.js fs. Таким чином , ви можете використовувати його точно так само , як ви б з fs.writeFileі fs.writeFileSync(як асинхронна і синхронна запис)

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