Создать каталог при записи в файл в Node.js

135

Я возился с 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 создавать структуру каталогов, если она не выходит для записи в файл?

Hirvesh
источник
1
fs.promises.mkdir(path.dirname("tmp/test.txt"), {recursive: true}).then(x => fs.promises.writeFile("tmp/test.txt", "Hey there!"))
Offenso 05

Ответы:

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 03
1
path- это также пакет, который должен быть обязательным, как fs: var path = require('path')на случай, если кому-то интересно. См. Документацию по узлу .
Рафаэль Эмшофф,
9
fs.existsSyncне является устаревшим , только fs.existsесть.
zzzzBov
6
Возникла некоторая путаница в отношении того, устарела ли функция fs.existsSync или нет. Сначала, насколько я понимаю, я думал, что это так, поэтому я обновил ответ, чтобы отразить это. Но теперь, как указывает @zzzzBov, в документации четко указано, что только fs.exists устарел, использование fs.existsSync по-прежнему допустимо. По этой причине я удалил предыдущий код, и теперь мой ответ содержит только более простое решение (с использованием fs.existsSync).
Tiago Peres França
1
@chrismarx представьте себе следующий путь: "/home/documents/a/b/c/myfile.txt". "/ home / documents" существует, а все перед ним - нет. Когда "sureDirectoryExistence" вызывается в первый раз, имя каталога - "/ home / documents / a / b / c". Я не могу сейчас вызвать fs.mkdirSync (dirname), потому что «/ home / documents / a / b» также не существует. Чтобы создать каталог «c», мне нужно сначала убедиться в существовании «/ home / documents / 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-extra .

lifeisfoo
источник
26

Бесстыдная пробка!

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

jrajav
источник
1
это создаст файл напрямую или только структуру каталогов? Я ищу решение, которое создает файл вместе со структурой каталогов при создании файла.
Hirvesh
Просто структура каталогов. Вы должны сначала указать 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);
}
micx
источник
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))
рекурсию
1
@Offenso это лучшее решение, но только для Node.js версии 10.12 и выше.
Nickensoul
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 () вернет false?
Джорджио Аресу
2
Использовать fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
рекурсию
1
@Offenso это не поддерживается узлом 8
Евгений Наида
2

Я опубликовал этот модуль только потому, что мне нужен был этот функционал.

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

Он работает как оболочка для методов fs в Node.js. Таким образом, вы можете использовать его точно так же, как и с fs.writeFileи fs.writeFileSync(как асинхронная, так и синхронная запись)

Кев
источник