Utwórz katalog podczas zapisywania do pliku w Node.js

152

Majstrowałem przy Node.js i znalazłem mały problem. Mam skrypt, który znajduje się w katalogu o nazwie data. Chcę, aby skrypt zapisywał dane do pliku w podkatalogu w tym datapodkatalogu. Jednak pojawia się następujący błąd:

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

Kod wygląda następująco:

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

Czy ktoś może mi pomóc w znalezieniu sposobu, aby Node.js utworzył strukturę katalogów, jeśli nie kończy się ona w celu zapisania do pliku?

Hirvesh
źródło
2
fs.promises.mkdir(path.dirname("tmp/test.txt"), {recursive: true}).then(x => fs.promises.writeFile("tmp/test.txt", "Hey there!"))
Offenso

Odpowiedzi:

141

Węzeł> 10.12.0

fs.mkdir akceptuje teraz taką { recursive: true }opcję:

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

lub z obietnicą:

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

Węzeł <= 10.11.0

Możesz rozwiązać ten problem za pomocą pakietu takiego jak mkdirp lub fs-extra . Jeśli nie chcesz instalować pakietu, zapoznaj się z odpowiedzią Tiago Peres França poniżej.

David Weldon
źródło
4
To ten, z którym idę ... te statystyki mnie przekonały.
Aran Mulholland
zauważ, że fs.promisesjest to nadal eksperymentalny nodejs.org/dist/latest-v10.x/docs/api/ ...
lasec0203
140

Jeśli nie chcesz używać żadnego dodatkowego pakietu, możesz wywołać następującą funkcję przed utworzeniem pliku:

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);
}
Tiago Peres França
źródło
2
Powinno to być używane statSynczamiast existsSync, w oparciu o stackoverflow.com/questions/4482686/ ...
GavinR
1
pathto także pakiet, który musi być wymagany tak jak fs: var path = require('path')na wypadek, gdyby ktoś się zastanawiał. Zobacz dokumentację węzła .
Rafael Emshoff,
9
fs.existsSyncnie jest przestarzała , tylko fs.existsjest.
zzzzBov
6
Pojawiło się pewne zamieszanie co do tego, czy funkcja fs.existsSync jest przestarzała, czy nie. Początkowo sądziłem, że tak jest, więc zaktualizowałem odpowiedź, aby to odzwierciedlić. Ale teraz, jak wskazał @zzzzBov, dokumentacja jasno stwierdza, że ​​tylko fs.exists został uznany za przestarzały, użycie fs.existsSync jest nadal aktualne. Z tego powodu usunąłem poprzedni kod i moja odpowiedź zawiera teraz tylko prostsze rozwiązanie (z użyciem fs.existsSync).
Tiago Peres França
1
@chrismarx wyobraź sobie następującą ścieżkę: „/home/documents/a/b/c/myfile.txt”. „/ home / documents” istnieje, a wszystko przed nim nie istnieje. Gdy „sureDirectoryExistence” jest wywoływana po raz pierwszy, nazwa katalogu to „/ home / documents / a / b / c”. Nie mogę teraz zadzwonić do fs.mkdirSync (dirname), ponieważ „/ home / documents / a / b” również nie istnieje. Aby utworzyć katalog „c”, muszę najpierw upewnić się, że istnieje „/ home / documents / a / b”.
Tiago Peres França,
45

Dzięki node-fs-extra możesz to łatwo zrobić.

Zainstaluj to

npm install --save fs-extra

Następnie użyj outputFilemetody. Jego dokumentacja mówi:

Prawie to samo, co writeFile (tj. Nadpisuje), z wyjątkiem tego, że jeśli katalog nadrzędny nie istnieje, zostaje utworzony.

Możesz go używać na trzy sposoby:

Styl oddzwaniania

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

Korzystanie z obietnic

Jeśli korzystasz z obietnic i mam nadzieję, że tak, to jest kod:

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

Wersja synchronizacji

Jeśli chcesz wersję do synchronizacji, użyj tego kodu:

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

Aby uzyskać pełne informacje, zapoznaj się z outputFiledokumentacją i wszystkimi obsługiwanymi metodami node-fs-extra .

lifeisfoo
źródło
26

Bezwstydny alarm wtyczki!

Będziesz musiał sprawdzić każdy katalog w wybranej strukturze ścieżek i utworzyć go ręcznie, jeśli nie istnieje. Wszystkie narzędzia do tego są już dostępne w module fs Node, ale możesz to zrobić po prostu za pomocą mojego modułu mkpath: https://github.com/jrajav/mkpath

jrajav
źródło
1
czy to utworzy plik bezpośrednio, czy tylko strukturę katalogów? Szukam rozwiązania, które utworzy plik wraz ze strukturą katalogów podczas tworzenia pliku.
Hirvesh
Tylko struktura katalogów. Najpierw należy wpisać mkdir / path i, jeśli nie było żadnych błędów, przystąpić do zapisywania pliku. Byłoby dość proste napisanie funkcji wykonującej obie funkcje jednocześnie, mając pełną ścieżkę do pliku do zapisania - po prostu oddziel
jrajav
1
Właściwie było to tak proste, że napisałem to w 2 minuty . :) (
nieprzetestowane
Aktualizacja: przetestowane i edytowane, spróbuj ponownie, jeśli nie zadziałało za pierwszym razem.
jrajav
8
@Kiyura Czym różni się to od powszechnie używanego mkdirp ?
David Weldon
9

Ponieważ nie mogę jeszcze komentować, zamieszczam ulepszoną odpowiedź opartą na fantastycznym rozwiązaniu @ tiago-peres-frança (dzięki!). Jego kod nie tworzy katalogu w przypadku, gdy w ścieżce brakuje tylko ostatniego katalogu, np. Wejście to „C: / test / abc”, a „C: / test” już istnieje. Oto fragment, który działa:

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

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

    fs.mkdirSync(filepath);
}
micx
źródło
1
Dzieje się tak, ponieważ rozwiązanie @ tiago wymaga ścieżki pliku . W twoim przypadku abcjest interpretowany jako plik, dla którego musisz utworzyć katalog. Aby również utworzyć abckatalog, dodaj do ścieżki przykładowy plik, np C:/test/abc/dummy.txt.
Sphinxxx
Użyj rekurencyjnego:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso
1
@Offenso to najlepsze rozwiązanie, ale tylko dla Node.js w wersji 10.12 i nowszych.
Nickensoul,
8

Moja rada brzmi: staraj się nie polegać na zależnościach, kiedy możesz to łatwo zrobić za pomocą kilku linijek kodu

Oto, co próbujesz osiągnąć w 14 wierszach kodu:

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);
    });
};
Alex C.
źródło
1
Czy trzecia linijka nie byłaby tak lepsza? return fs.lstatSync(dpath).isDirectory(), w przeciwnym razie co by się stało, gdyby isDirectory () zwróciło false?
Giorgio Aresu
2
Użyj rekurencyjnego:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso
1
@Offenso nie jest obsługiwany przez węzeł 8
Ievgen Naida
2

Właśnie opublikowałem ten moduł, ponieważ potrzebowałem tej funkcjonalności.

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

Działa jak wrapper wokół metod Node.js fs. Możesz więc używać go dokładnie w taki sam sposób, jak z fs.writeFilei fs.writeFileSync(zarówno asynchroniczne, jak i synchroniczne zapisy)

Kev
źródło
0

najpierw pobiera pełną ścieżkę łącznie z katalogiem i rozpakowuje katalog

//Just for the sake of example
cwd=process.cwd()
filendir=path.resolve(cwd,'_site/assets/text','node.txt')

// Extracting directory name
mkdir=path.dirname(filendir)

Teraz utwórz katalog, dodaj opcję rekurencyjną: true, jak stwierdził @David Weldon

fs.mkdirSync(mkdir,{recursive:true})

Następnie utwórz plik

data='Some random text'
fs.writeFileSync(filendir,data)
Hank W.
źródło