Crea directory durante la scrittura su file in Node.js


135

Ho armeggiato con Node.js e ho riscontrato un piccolo problema. Ho uno script che risiede in una directory chiamata data. Voglio che lo script scriva alcuni dati in un file in una sottodirectory all'interno della datasottodirectory. Tuttavia sto ottenendo il seguente errore:

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

Il codice è il seguente:

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

Qualcuno può aiutarmi a scoprire come fare in modo che Node.js crei la struttura di directory se non esce per scrivere su un file?


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

Risposte:


127

Nodo> 10.12.0

fs.mkdir ora accetta { recursive: true }un'opzione come questa:

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

o con una promessa:

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

Nodo <= 10.11.0

Puoi risolverlo con un pacchetto come mkdirp o fs-extra . Se non si desidera installare un pacchetto, vedere la risposta di Tiago Peres França di seguito.


4
È quello con cui vado ... quelle statistiche mi hanno conquistato.
Aran Mulholland,

nota che fs.promisesè ancora un nodo
lasec0203

132

Se non si desidera utilizzare alcun pacchetto aggiuntivo, è possibile chiamare la seguente funzione prima di creare il file:

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
Questo dovrebbe usare statSyncal posto di existsSync, sulla base di stackoverflow.com/questions/4482686/...
GavinR

1
pathè anche un pacchetto che deve essere richiesto proprio come fs: var path = require('path')nel caso qualcuno si stia chiedendo. Vedi la documentazione del nodo .
Rafael Emshoff,

9
fs.existsSyncnon è deprecato , lo fs.existsè solo .
zzzzBov,

6
C'è stata una certa confusione sul fatto che la funzione fs.existsSync sia stata deprecata o meno. All'inizio, secondo la mia comprensione, ho pensato che lo fosse, quindi ho aggiornato la risposta per riflettere questo. Ma ora, come sottolineato da @zzzzBov, la documentazione afferma chiaramente che solo fs.exists è stato deprecato, l'uso di fs.existsSync è ancora valido. Per questo motivo ho eliminato il codice precedente e la mia risposta ora contiene solo la soluzione più semplice (con l'uso di fs.existsSync).
Tiago Peres França,

1
@chrismarx immagina il seguente percorso: "/home/documents/a/b/c/myfile.txt". "/ home / documenti" esiste, mentre tutto ciò che lo precede non esiste. Quando si chiama "sureDirectoryExistence" per la prima volta, dirname è "/ home / documenti / a / b / c". Non posso chiamare fs.mkdirSync (dirname) in questo momento perché anche "/ home / documenti / a / b" non esiste. Per creare la directory "c", devo prima assicurare l'esistenza di "/ home / documenti / a / b".
Tiago Peres França,

43

Con node-fs-extra puoi farlo facilmente.

Installalo

npm install --save fs-extra

Quindi utilizzare il outputFilemetodo La sua documentazione dice:

Quasi uguale a writeFile (ovvero sovrascrive), tranne per il fatto che se la directory padre non esiste, viene creata.

Puoi usarlo in tre modi:

Stile di richiamata

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

Usando le promesse

Se usi le promesse , e lo spero, questo è il codice:

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

Sincronizza versione

Se vuoi una versione di sincronizzazione, usa questo codice:

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

Per un riferimento completo, consultare la outputFiledocumentazione e tutti i metodi supportati da node-fs-extra .


26

Avviso spina spudorato!

Dovrai controllare ogni directory nella struttura del percorso che desideri e crearla manualmente se non esiste. Tutti gli strumenti per farlo sono già presenti nel modulo fs di Node, ma puoi farlo semplicemente con il mio modulo mkpath: https://github.com/jrajav/mkpath


1
questo creerà direttamente il file o solo la struttura della directory? Sto cercando una soluzione che crei il file insieme alla struttura della directory durante la creazione del file.
Hirvesh,

Solo la struttura delle directory. Dovresti prima mkdir / path e, se non ci sono errori, procedi alla scrittura del tuo file. Sarebbe abbastanza semplice scrivere una funzione per fare entrambe le cose contemporaneamente, dato un percorso completo a un file da scrivere - basta dividere il nome del file usando path.basename
jrajav

1
In effetti, era così semplice che l'ho scritto in 2 minuti . :) (Non testato)
jrajav

Aggiornamento: testato e modificato, riprovare se non ha funzionato la prima volta.
jrajav

8
@Kiyura In cosa differisce dal mkdirp ampiamente usato ?
David Weldon,

9

Dal momento che non posso ancora commentare, sto pubblicando una risposta avanzata basata sulla fantastica soluzione @ tiago-peres-frança (grazie!). Il suo codice non crea una directory nel caso in cui nel percorso manchi solo l'ultima directory, ad esempio l'input è "C: / test / abc" e "C: / test" esiste già. Ecco uno snippet che funziona:

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

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

    fs.mkdirSync(filepath);
}

1
Questo perché la soluzione di @ tiago prevede un percorso di file . Nel tuo caso, abcviene interpretato come il file per cui devi creare una directory. Per creare anche la abcdirectory, aggiungi un file fittizio al tuo percorso, ad es C:/test/abc/dummy.txt.
Sphinxxx,

Usa ricorsivo:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso il

1
@Offenso è la soluzione migliore, ma solo per Node.js versione 10.12 e successive.
Nickensoul,

8

Il mio consiglio è: cerca di non fare affidamento sulle dipendenze quando puoi farlo facilmente con poche righe di codice

Ecco cosa stai cercando di ottenere 14 righe di codice:

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
La terza linea non sarebbe migliore in questo modo? return fs.lstatSync(dpath).isDirectory(), altrimenti cosa accadrebbe se isDirectory () restituisce false?
Giorgio Aresu,

2
Usa ricorsivo:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso il

1
@Offenso non è supportato da un nodo 8
Ievgen Naida,

2

Ho appena pubblicato questo modulo perché avevo bisogno di questa funzionalità.

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

Funziona come un wrapper per i metodi fs di Node.js. Quindi puoi usarlo esattamente come faresti con fs.writeFilee fs.writeFileSync(sia in scrittura asincrona che sincrona)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.