Créer un répertoire lors de l'écriture dans un fichier dans Node.js


135

J'ai bricolé Node.js et j'ai trouvé un petit problème. J'ai un script qui réside dans un répertoire appelé data. Je veux que le script écrive des données dans un fichier dans un sous-répertoire dans le datasous - répertoire. Cependant, j'obtiens l'erreur suivante:

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

Le code est comme suit:

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

Quelqu'un peut-il m'aider à savoir comment faire en sorte que Node.js crée la structure de répertoires s'il ne sort pas pour écrire dans un fichier?


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

Réponses:


127

Nœud> 10.12.0

fs.mkdir accepte maintenant une { recursive: true }option comme celle-ci:

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

ou avec une promesse:

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

Nœud <= 10.11.0

Vous pouvez résoudre ce problème avec un package comme mkdirp ou fs-extra . Si vous ne souhaitez pas installer de package, veuillez consulter la réponse de Tiago Peres França ci-dessous.


4
C'est celui avec lequel je vais ... ces statistiques m'ont convaincu.
Aran Mulholland

notez que fs.promisesc'est toujours expérimental nodejs.org/dist/latest-v10.x/docs/api/…
lasec0203

132

Si vous ne souhaitez pas utiliser de package supplémentaire, vous pouvez appeler la fonction suivante avant de créer votre fichier:

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
Cela devrait utiliser à la statSyncplace de existsSync, basé sur stackoverflow.com/questions/4482686/…
GavinR

1
pathest également un package qui doit être requis tout comme fs: var path = require('path')au cas où quelqu'un se demanderait. Voir la documentation des nœuds .
Rafael Emshoff

9
fs.existsSyncn'est pas obsolète , seulement fs.exists.
zzzzBov

6
Il y a eu une certaine confusion quant à savoir si la fonction fs.existsSync est obsolète ou non. Au début, d'après ce que j'ai compris, je pensais que c'était le cas, alors j'ai mis à jour la réponse pour refléter cela. Mais maintenant, comme l'a souligné @zzzzBov, la documentation indique clairement que seul fs.exists est obsolète, l'utilisation de fs.existsSync est toujours valide. Pour cette raison, j'ai supprimé le code précédent et ma réponse ne contient plus que la solution la plus simple (avec l'utilisation de fs.existsSync).
Tiago Peres França

1
@chrismarx imaginez le chemin suivant: "/home/documents/a/b/c/myfile.txt". "/ home / documents" existe, alors que tout ce qui se trouve devant lui n'existe pas. Lorsque "ensureDirectoryExistence" est appelé pour la première fois, dirname est "/ home / documents / a / b / c". Je ne peux pas appeler fs.mkdirSync (dirname) pour le moment car "/ home / documents / a / b" n'existe pas non plus. Pour créer le répertoire "c", je dois d'abord m'assurer de l'existence de "/ home / documents / a / b".
Tiago Peres França

43

Avec node-fs-extra, vous pouvez le faire facilement.

Installez-le

npm install --save fs-extra

Ensuite, utilisez la outputFileméthode. Sa documentation dit:

Presque identique à writeFile (c'est-à-dire qu'il écrase), sauf que si le répertoire parent n'existe pas, il est créé.

Vous pouvez l'utiliser de trois manières:

Style de rappel

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

Utiliser les promesses

Si vous utilisez des promesses , et je l'espère, voici le code:

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

Version de synchronisation

Si vous voulez une version de synchronisation, utilisez simplement ce code:

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

Pour une référence complète, consultez la outputFiledocumentation et toutes les méthodes prises en charge par node-fs-extra .


26

Alerte de prise sans vergogne!

Vous devrez vérifier chaque répertoire dans la structure de chemin que vous souhaitez et le créer manuellement s'il n'existe pas. Tous les outils pour le faire sont déjà présents dans le module fs de Node, mais vous pouvez faire tout cela simplement avec mon module mkpath: https://github.com/jrajav/mkpath


1
cela créera-t-il le fichier directement ou simplement la structure du répertoire? Je recherche une solution qui crée le fichier avec la structure du répertoire lors de la création du fichier.
Hirvesh

Juste la structure des répertoires. Vous devez d'abord le chemin mkdir / et, s'il n'y avait aucune erreur, procéder à l'écriture de votre fichier. Il serait assez simple d'écrire une fonction pour faire les deux simultanément, étant donné un chemin complet vers un fichier à écrire - il suffit de séparer le nom du fichier en utilisant path.basename
jrajav

1
En fait, c'était si simple que je l'ai écrit en 2 minutes . :) (Non testé)
jrajav

Mise à jour: testée et modifiée, veuillez réessayer si cela n'a pas fonctionné la première fois.
jrajav

8
@Kiyura En quoi est-ce différent du mkdirp largement utilisé ?
David Weldon

9

Comme je ne peux pas encore commenter, je poste une réponse améliorée basée sur la solution fantastique @ tiago-peres-frança (merci!). Son code ne crée pas de répertoire dans le cas où seul le dernier répertoire est manquant dans le chemin, par exemple l'entrée est "C: / test / abc" et "C: / test" existe déjà. Voici un extrait qui fonctionne:

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

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

    fs.mkdirSync(filepath);
}

1
C'est parce que la solution de @ tiago attend un chemin de fichier . Dans votre cas, abcest interprété comme le fichier pour lequel vous devez créer un répertoire. Pour créer également le abcrépertoire, ajoutez un fichier factice à votre chemin, par exemple C:/test/abc/dummy.txt.
Sphinxxx

Utilisation récursive:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

1
@Offenso c'est la meilleure solution, mais uniquement pour la version 10.12 et supérieure de Node.js.
Nickensoul

8

Mon conseil est: essayez de ne pas vous fier aux dépendances lorsque vous pouvez le faire facilement avec quelques lignes de codes

Voici ce que vous essayez de réaliser en 14 lignes de code:

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 troisième ligne ne serait-elle pas meilleure comme ça? return fs.lstatSync(dpath).isDirectory(), sinon que se passerait-il si isDirectory () renvoie false?
Giorgio Aresu

2
Utilisation récursive:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

1
@Offenso il n'est pas pris en charge par un nœud 8
Ievgen Naida

2

Je viens de publier ce module car j'avais besoin de cette fonctionnalité.

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

Cela fonctionne comme un wrapper autour des méthodes fs de Node.js. Vous pouvez donc l'utiliser exactement de la même manière que vous le feriez avec fs.writeFileet fs.writeFileSync(écritures asynchrones et synchrones)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.