Comment écrire un fichier si le dossier parent n'existe pas?


93

J'ai besoin d'écrire le fichier dans le chemin suivant:

fs.writeFile('/folder1/folder2/file.txt', 'content', function () {…});

Mais le '/folder1/folder2'chemin n'existe peut-être pas. Donc j'obtiens l'erreur suivante:

message = ENOENT, ouvrez /folder1/folder2/file.txt

Comment puis-je écrire du contenu sur ce chemin?


2
fs.promises.mkdir(path.dirname('/folder1/folder2/file.txt'), {recursive: true}).then(x => fs.promises.writeFile('/folder1/folder2/file.txt', 'content'))
Offenso

Réponses:


127

Utilisez mkdirp en combinaison avec path.dirnamefirst.

var mkdirp = require('mkdirp');
var fs = require('fs');
var getDirName = require('path').dirname;

function writeFile(path, contents, cb) {
  mkdirp(getDirName(path), function (err) {
    if (err) return cb(err);

    fs.writeFile(path, contents, cb);
  });
}

Si tout le chemin existe déjà, mkdirpc'est un noop. Sinon, il crée tous les répertoires manquants pour vous.

Ce module fait ce que vous voulez: https://npmjs.org/package/writefile . Je l'ai obtenu en recherchant sur Google "writefile mkdirp". Ce module renvoie une promesse au lieu de prendre un rappel, alors assurez-vous de lire d'abord une introduction aux promesses. Cela pourrait en fait compliquer les choses pour vous.

La fonction que j'ai donnée fonctionne en tout cas.


Donc, si nous voulons attendre qu'il se termine, nous devons tout mettre après cela dans le rappel? Y a-t-il un autre moyen?
pete

@pete si vous utilisez babel, vous pouvez utiliser async / await
Lucas Reppe Welander

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

27

Je trouve que le moyen le plus simple de le faire est d'utiliser la méthode outputFile () du module fs-extra .

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

Exemple:

var fs = require('fs-extra');
var file = '/tmp/this/path/does/not/exist/file.txt'

fs.outputFile(file, 'hello!', function (err) {
    console.log(err); // => null

    fs.readFile(file, 'utf8', function (err, data) {
        console.log(data); // => hello!
    });
});

Il a également promis un support prêt à l'emploi ces jours-ci!


19

Peut-être plus simplement, vous pouvez simplement utiliser le module npm fs-path .

Votre code ressemblerait alors à:

var fsPath = require('fs-path');

fsPath.writeFile('/folder1/folder2/file.txt', 'content', function(err){
  if(err) {
    throw err;
  } else {
    console.log('wrote a file like DaVinci drew machines');
  }
});

18

Éditer

La version NodeJS 10.12.0a ajouté un support natif pour les deux mkdiret mkdirSyncpour créer le directeur parent de manière récursive avec l' recursive: trueoption comme suit:

fs.mkdirSync(targetDir, { recursive: true });

Et si tu préfères fs Promises API, tu peux écrire

fs.promises.mkdir(targetDir, { recursive: true });

Réponse originale

Créez les répertoires parents de manière récursive s'ils n'existent pas! ( Zéro dépendances )

const fs = require('fs');
const path = require('path');

function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
  const sep = path.sep;
  const initDir = path.isAbsolute(targetDir) ? sep : '';
  const baseDir = isRelativeToScript ? __dirname : '.';

  return targetDir.split(sep).reduce((parentDir, childDir) => {
    const curDir = path.resolve(baseDir, parentDir, childDir);
    try {
      fs.mkdirSync(curDir);
    } catch (err) {
      if (err.code === 'EEXIST') { // curDir already exists!
        return curDir;
      }

      // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
      if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.
        throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
      }

      const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
      if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) {
        throw err; // Throw if it's just the last created dir.
      }
    }

    return curDir;
  }, initDir);
}

Usage

// Default, make directories relative to current working directory.
mkDirByPathSync('path/to/dir');

// Make directories relative to the current script.
mkDirByPathSync('path/to/dir', {isRelativeToScript: true});

// Make directories with an absolute path.
mkDirByPathSync('/path/to/dir');

Démo

Essayez-le!

Explications

  • [UPDATE] Cette solution gère les erreurs spécifiques à la plate-forme comme EISDIRpour Mac et EPERMet EACCESpour Windows.
  • Cette solution gère à la fois les chemins relatifs et absolus .
  • Dans le cas de chemins relatifs, les répertoires cibles seront créés (résolus) dans le répertoire de travail courant. Pour les résoudre par rapport au répertoire de script actuel, passez {isRelativeToScript: true}.
  • Utiliser path.sepet path.resolve(), pas seulement la /concaténation, pour éviter les problèmes multiplateformes.
  • Utilisation fs.mkdirSyncet gestion de l'erreur avec try/catchif levée pour gérer les conditions de concurrence: un autre processus peut ajouter le fichier entre les appels à fs.existsSync()et fs.mkdirSync()et provoquer une exception.
    • L'autre moyen d'y parvenir pourrait être de vérifier si un fichier existe, puis de le créer, c'est-à-dire if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. Mais c'est un anti-pattern qui laisse le code vulnérable aux conditions de course.
  • Nécessite Node v6 et plus récent pour prendre en charge la déstructuration. (Si vous rencontrez des problèmes pour implémenter cette solution avec d'anciennes versions de Node, laissez-moi un commentaire)

3

Vous pouvez utiliser

fs.stat('/folder1/folder2', function(err, stats){ ... });

statsest un fs.Statstype d'objet, vous pouvez vérifier stats.isDirectory(). En fonction de l'examen de erret statsvous pouvez faire quelque chose, fs.mkdir( ... )ou lancer une erreur.

Référence

Mise à jour: correction des virgules dans le code.


Je ne peux donc pas écrire de fichier en utilisant la commande sibgle dans nodejs?
Erik

2

Voici ma fonction personnalisée pour créer de manière récursive des répertoires (sans dépendances externes):

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

var myMkdirSync = function(dir){
    if (fs.existsSync(dir)){
        return
    }

    try{
        fs.mkdirSync(dir)
    }catch(err){
        if(err.code == 'ENOENT'){
            myMkdirSync(path.dirname(dir)) //create parent dir
            myMkdirSync(dir) //create dir
        }
    }
}

myMkdirSync(path.dirname(filePath));
var file = fs.createWriteStream(filePath);

2

Voici ma fonction qui fonctionne dans Node 10.12.0. J'espère que cela aidera.

const fs = require('fs');
function(dir,filename,content){
        fs.promises.mkdir(dir, { recursive: true }).catch(error => { console.error('caught exception : ', error.message); });
        fs.writeFile(dir+filename, content, function (err) {
            if (err) throw err;
            console.info('file saved!');
        });
    }

2

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

Installez-le

npm install --save fs-extra

Utilisez ensuite la méthode outputFile au lieu de writeFileSync

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

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

0

Voici une partie de la réponse de Myrne Stol présentée comme une réponse distincte:

Ce module fait ce que vous voulez: https://npmjs.org/package/writefile . Je l'ai obtenu en recherchant sur Google "writefile mkdirp". Ce module renvoie une promesse au lieu de prendre un rappel, alors assurez-vous de lire d'abord une introduction aux promesses. Cela pourrait en fait compliquer les choses pour vous.


0
let name = "./new_folder/" + file_name + ".png";
await driver.takeScreenshot().then(
  function(image, err) {
    require('mkdirp')(require('path').dirname(name), (err) => {
      require('fs').writeFile(name, image, 'base64', function(err) {
        console.log(err);
      });
    });
  }
);

Les réponses basées uniquement sur le code sont considérées comme de mauvaise qualité: assurez-vous de fournir une explication de ce que fait votre code et de la manière dont il résout le problème. Cela aidera le demandeur et les futurs lecteurs si vous pouvez ajouter plus d'informations dans votre message. Voir Expliquer les réponses entièrement basées sur le code
Calos
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.