Comment créer un répertoire s'il n'existe pas en utilisant Node.js?

657

Est-ce la bonne façon de créer un répertoire s'il n'existe pas. Il doit avoir la permission complète pour le script et être lisible par les autres.

var dir = __dirname + '/upload';
if (!path.existsSync(dir)) {
    fs.mkdirSync(dir, 0744);
}
Whisher
la source
1
Copie possible du dossier de création Node.js ou utilisation existante
Benny Neugebauer

Réponses:

1281
var fs = require('fs');
var dir = './tmp';

if (!fs.existsSync(dir)){
    fs.mkdirSync(dir);
}
chovy
la source
28
Si vous effectuez cette opération au démarrage ou à l'initialisation de l'application, il est très bien de bloquer l'exécution car vous feriez la même chose si vous le faisiez en mode asynchrone. Si vous créez un répertoire comme une opération récurrente, sa mauvaise pratique, mais ne causera probablement aucun problème de performances, mais c'est néanmoins un mauvais habbit. À utiliser uniquement pour démarrer votre application ou autrement pour des opérations ponctuelles.
tsturzl
20
existSync () n'est pas obsolète, existe () est cependant - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick
utiliser les Syncméthodes * est généralement un non-non: ne pas vouloir bloquer la boucle d'événements
Max Heiber
14
L'utilisation de méthodes de synchronisation est très bien pour les scripts locaux et autres, ce n'est évidemment pas une bonne idée pour un serveur.
Pier
Si ce bloc est entouré de setTimeout, c'est async .....................
Bryan Grace
186

Non, pour plusieurs raisons.

  1. Le pathmodule n'a pas de méthode exists/ existsSync. C'est dans le fsmodule. (Peut-être que vous venez de faire une faute de frappe dans votre question?)

  2. Les documents vous découragent explicitement d'utiliser exists.

    fs.exists()est un anachronisme et n'existe que pour des raisons historiques. Il ne devrait presque jamais y avoir de raison de l'utiliser dans votre propre code.

    En particulier, vérifier si un fichier existe avant de l'ouvrir est un anti-pattern qui vous laisse vulnérable aux conditions de concurrence: un autre processus peut supprimer le fichier entre les appels à fs.exists()et fs.open(). Ouvrez simplement le fichier et gérez l'erreur lorsqu'il n'est pas là.

    Puisque nous parlons d'un répertoire plutôt que d'un fichier, ce conseil implique que vous devez simplement appeler mkdiret ignorer inconditionnellement EEXIST.

  3. En général, vous devez éviter les Syncméthodes * . Ils bloquent, ce qui signifie que rien d'autre dans votre programme ne peut se produire pendant que vous allez sur le disque. Il s'agit d'une opération très coûteuse, et le temps qu'il faut brise l'hypothèse de base de la boucle d'événements du nœud.

    Les Syncméthodes * sont généralement bien dans les scripts rapides à usage unique (ceux qui font une chose puis se terminent), mais ne devraient presque jamais être utilisés lorsque vous écrivez un serveur: votre serveur ne pourra répondre à personne pendant toute la durée des demandes d'E / S. Si plusieurs demandes client nécessitent des opérations d'E / S, votre serveur s'arrêtera très rapidement.


    La seule fois où j'envisagerais d'utiliser les Syncméthodes * dans une application serveur est une opération qui se produit une fois (et une seule fois), au démarrage. Par exemple, require utilise réellementreadFileSync pour charger des modules.

    Même dans ce cas, vous devez toujours être prudent car de nombreuses E / S synchrones peuvent ralentir inutilement le temps de démarrage de votre serveur.


    Au lieu de cela, vous devez utiliser les méthodes d'E / S asynchrones.

Donc, si nous rassemblons ces conseils, nous obtenons quelque chose comme ceci:

function ensureExists(path, mask, cb) {
    if (typeof mask == 'function') { // allow the `mask` parameter to be optional
        cb = mask;
        mask = 0777;
    }
    fs.mkdir(path, mask, function(err) {
        if (err) {
            if (err.code == 'EEXIST') cb(null); // ignore the error if the folder already exists
            else cb(err); // something else went wrong
        } else cb(null); // successfully created folder
    });
}

Et nous pouvons l'utiliser comme ceci:

ensureExists(__dirname + '/upload', 0744, function(err) {
    if (err) // handle folder creation error
    else // we're all good
});

Bien sûr, cela ne tient pas compte des cas marginaux comme

  • Que se passe-t-il si le dossier est supprimé pendant l'exécution de votre programme? (en supposant que vous vérifiez seulement qu'il existe une fois au démarrage)
  • Que se passe-t-il si le dossier existe déjà mais avec les mauvaises autorisations?
josh3736
la source
1
existe-t-il un moyen d'éviter SyntaxError: les littéraux octaux ne sont pas autorisés en mode strict?
Whisher
8
Écrivez-le sous forme décimale. 0744 == 484.
josh3736
3
Une alternative consiste à utiliser un module qui étend fs pour avoir cette fonctionnalité comme github.com/jprichardson/node-fs-extra
Bret
ce drapeau "masque" est-il toujours d'actualité en 2019? quel en était le but?
oldboy
C'est le mode de fichier Unix - les autorisations de lecture / écriture du répertoire.
josh3736
44

J'ai trouvé et module npm qui fonctionne comme un charme pour cela. Il suffit de faire un mkdir récursivement lorsque cela est nécessaire, comme un "mkdir -p".

https://www.npmjs.com/package/mkdirp

Toni Gamez
la source
34

La mkdirméthode a la capacité de créer récursivement tous les répertoires d'un chemin qui n'existent pas et d'ignorer ceux qui existent.

Depuis les documents Node v10 / 11 :

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

REMARQUE: vous devez d'abord importer le fsmodule intégré.

Voici maintenant un exemple un peu plus robuste qui exploite les modules ES natifs (avec indicateur activé et extension .mjs), gère les chemins non root et prend en compte les chemins d'accès complets:

import fs from 'fs';
import path from 'path';

createDirectories(pathname) {
   const __dirname = path.resolve();
   pathname = pathname.replace(/^\.*\/|\/?[^\/]+\.[a-z]+|\/$/g, ''); // Remove leading directory markers, and remove ending /file-name.extension
   fs.mkdir(path.resolve(__dirname, pathname), { recursive: true }, e => {
       if (e) {
           console.error(e);
       } else {
           console.log('Success');
       }
    });
}

Vous pouvez l'utiliser comme createDirectories('/components/widget/widget.js');.

Et bien sûr, vous voudrez probablement devenir plus sophistiqué en utilisant des promesses avec async / wait pour tirer parti de la création de fichiers de manière synchrone plus lisible lorsque les répertoires sont créés; mais, cela dépasse la portée de la question.

un peu moins
la source
1
Pourquoi const __dirname = path.resolve (); et ne pas utiliser le __dirname intégré?
TamusJRoyce
29

Juste au cas où toute personne intéressée par la version à une ligne. :)

//or in typescript: import * as fs from 'fs';
const fs = require('fs');
!fs.existsSync(dir) && fs.mkdirSync(dir);
LeOn - Han Li
la source
Allégé 1-liner pas réellement 1 ligne.
Développement web hybride le
20

Vous pouvez simplement utiliser mkdiret intercepter l'erreur si le dossier existe.
C'est asynchrone (donc meilleure pratique) et sûr.

fs.mkdir('/path', err => { 
    if (err && err.code != 'EEXIST') throw 'up'
    .. safely do your stuff here  
    })

(Ajoutez éventuellement un deuxième argument avec le mode.)


D'autres pensées:

  1. Vous pouvez alors utiliser ou attendre en utilisant natif promisify .

    const util = require('util'), fs = require('fs');
    const mkdir = util.promisify(fs.mkdir);
    var myFunc = () => { ..do something.. } 
    
    mkdir('/path')
        .then(myFunc)
        .catch(err => { if (err.code != 'EEXIST') throw err; myFunc() })
  2. Vous pouvez faire votre propre méthode de promesse, quelque chose comme (non testé):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Pour la vérification synchrone, vous pouvez utiliser:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. Ou vous pouvez utiliser une bibliothèque, les deux plus populaires étant

SamGoody
la source
1
pour l'approche prometteuse n ° 1, vous pouvez réorganiser la capture. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
Ce qui serait cool
Et utiliser à la !==place de!=
Quentin Roy
18

Avec le package fs-extra , vous pouvez le faire avec une doublure :

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

const dir = '/tmp/this/path/does/not/exist';
fs.ensureDirSync(dir);
galki
la source
Une telle réponse sous-estimée! fs-extra a bacame un must have pour moi. Je pense que c'est une abréviation d'écrire 10+ lignes juste pour vérifier si un dossier existe ...
538ROMEO
10

La meilleure solution serait d'utiliser le module npm appelé node-fs-extra . Il a une méthode appelée mkdirqui crée le répertoire que vous avez mentionné. Si vous donnez un long chemin de répertoire, il créera automatiquement les dossiers parents. Le module est un super ensemble de modules npm fs, vous pouvez donc utiliser toutes les fonctions fségalement si vous ajoutez ce module.

Abdul Vajid
la source
6
var dir = 'path/to/dir';
try {
  fs.mkdirSync(dir);
} catch(e) {
  if (e.code != 'EEXIST') throw e;
}
Ping.Goblue
la source
4
Pour Node.js v7.4.0, la documentation indique qu'elle fs.exists()est obsolète, mais fs.existsSync()ne l'est pas. Pourriez-vous ajouter un lien vers une ressource disant qui fs.existsSync()est dépréciée?
francis
1
Les réponses uniquement codées ne sont pas très utiles aux utilisateurs qui viendront à cette question à l'avenir. Veuillez modifier votre réponse pour expliquer pourquoi votre code résout le problème d'origine
yivi
3
@francis, hmm, je regardais Node.js v5, nodejs.org/docs/latest-v5.x/api/fs.html#fs_fs_existssync_path
Ping.Goblue
1
Merci! Il semble que la fonction existait dans la version 0.12, s'est déconseillée dans les versions 4 et 5 et a été restaurée dans les versions 6 et 7 ... Une sorte de fonction zombi ...
francis
1
Oui, apparemment, il n'est PAS obsolète à partir de Apr 2018: nodejs.org/api/fs.html#fs_fs_existssync_path
LeOn - Han Li
5
    var filessystem = require('fs');
    var dir = './path/subpath/';

    if (!filessystem.existsSync(dir)){
        filessystem.mkdirSync(dir);
    }else
    {
        console.log("Directory already exist");
    }

Cela peut vous aider :)

Vishnu S Babu
la source
5

ENOENT: aucun fichier ou répertoire de ce type

Solution

const fs = require('fs')  // in javascript
import * as fs from "fs"  // in typescript
import fs from "fs"       // in typescript

// it will create the directory if it does not exist.
!fs.existsSync(`./assets/`) && fs.mkdirSync(`./assets/`, { recursive: true })
WasiF
la source
1
cela fonctionne, merci
Aljohn Yamaro
3

Je voudrais ajouter un refactoriseur Typescript Promise de la réponse de josh3736 .

Il fait la même chose et a les mêmes cas de bord, il se trouve qu'il utilise juste des promesses, des typedefs dactylographiés et fonctionne avec "use strict".

// https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation
const allRWEPermissions = parseInt("0777", 8);

function ensureFilePathExists(path: string, mask: number = allRWEPermissions): Promise<void> {
    return new Promise<void>(
        function(resolve: (value?: void | PromiseLike<void>) => void,
            reject: (reason?: any) => void): void{
            mkdir(path, mask, function(err: NodeJS.ErrnoException): void {
                if (err) {
                    if (err.code === "EEXIST") {
                        resolve(null); // ignore the error if the folder already exists
                    } else {
                        reject(err); // something else went wrong
                    }
                } else {
                    resolve(null); // successfully created folder
                }
            });
    });
}
Nathan Cooper
la source
3

Avec Node 10 + ES6:

import path from 'path';
import fs from 'fs';

(async () => {
  const dir = path.join(__dirname, 'upload');

  try {
    await fs.promises.mkdir(dir);
  } catch (error) {
    if (error.code === 'EEXIST') {
      // Something already exists, but is it a file or directory?
      const lstat = await fs.promises.lstat(dir);

      if (!lstat.isDirectory()) {
        throw error;
      }
    } else {
      throw error;
    }
  }
})();
sdgfsdh
la source
2

Vous pouvez utiliser la commande noeud File System fs.stat pour vérifier si dir existe et fs.mkdir pour créer un répertoire avec rappel, ou fs.mkdirSync pour créer un répertoire sans rappel, comme cet exemple:

//first require fs
const fs = require('fs');

// Create directory if not exist (function)
const createDir = (path) => {
    // check if dir exist
    fs.stat(path, (err, stats) => {
        if (stats.isDirectory()) {
            // do nothing
        } else {
            // if the given path is not a directory, create a directory
            fs.mkdirSync(path);
        }
    });
};
majid jiji
la source
1

Voici une petite fonction pour créer récursivement des répertoires:

const createDir = (dir) => {
  // This will create a dir given a path such as './folder/subfolder' 
  const splitPath = dir.split('/');
  splitPath.reduce((path, subPath) => {
    let currentPath;
    if(subPath != '.'){
      currentPath = path + '/' + subPath;
      if (!fs.existsSync(currentPath)){
        fs.mkdirSync(currentPath);
      }
    }
    else{
      currentPath = subPath;
    }
    return currentPath
  }, '')
}
MrBlenny
la source
0

Utiliser async / wait:

const mkdirP = async (directory) => {
  try {
    return await fs.mkdirAsync(directory);
  } catch (error) {
    if (error.code != 'EEXIST') {
      throw e;
    }
  }
};

Vous devrez promettre fs:

import nodeFs from 'fs';
import bluebird from 'bluebird';

const fs = bluebird.promisifyAll(nodeFs);
sdgfsdh
la source