Come creare una directory se non esiste usando Node.js?


Risposte:


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

if (!fs.existsSync(dir)){
    fs.mkdirSync(dir);
}

28
Se stai eseguendo questa operazione all'avvio o all'inizializzazione dell'app, va bene bloccare l'esecuzione poiché faresti la stessa cosa se dovessi farlo in modo asincrono. Se stai creando una directory come un'operazione ricorrente, allora è una cattiva pratica ma probabilmente non causerà alcun problema di prestazioni, ma è comunque una cattiva abitudine. Utilizzare solo per l'avvio dell'app o altrimenti operazioni una tantum.
tsturzl,

20
esisteSync () non è deprecato, esiste () è però - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick,

usare * Syncmetodi è di solito un no-no: non voglio bloccare il ciclo degli eventi
Max Heiber

14
L'uso dei metodi di sincronizzazione va bene per gli script locali e simili, ovviamente non è una buona idea per un server.
Pier,

Se quel blocco è circondato da setTimeout, è asincrono .....................
Bryan Grace,

186

No, per diversi motivi.

  1. Il pathmodulo non ha un metodo exists/ existsSync. È nel fsmodulo. (Forse hai appena fatto un refuso nella tua domanda?)

  2. I documenti ti scoraggiano esplicitamente dall'uso exists.

    fs.exists()è un anacronismo ed esiste solo per ragioni storiche. Non dovrebbe esserci quasi mai un motivo per usarlo nel tuo codice.

    In particolare, controllare se esiste un file prima di aprirlo è un anti-pattern che ti rende vulnerabile alle condizioni di gara: un altro processo può rimuovere il file tra le chiamate a fs.exists()e fs.open(). Basta aprire il file e gestire l'errore quando non è presente.

    Dato che stiamo parlando di una directory piuttosto che di un file, questo consiglio implica che dovresti semplicemente chiamare mkdire ignorare incondizionatamente EEXIST.

  3. In generale, dovresti evitare i Syncmetodi * . Stanno bloccando, il che significa che non può succedere nient'altro nel tuo programma mentre vai sul disco. Questa è un'operazione molto costosa e il tempo impiegato interrompe il presupposto fondamentale del ciclo di eventi del nodo.

    I Syncmetodi * di solito vanno bene negli script rapidi monouso (quelli che fanno una cosa e poi escono), ma non dovrebbero quasi mai essere usati quando si scrive un server: il server non sarà in grado di rispondere a nessuno per l'intera durata delle richieste I / O. Se più richieste client richiedono operazioni di I / O, il server si arresterà molto rapidamente.


    L'unica volta che prenderei in considerazione l'uso dei Syncmetodi * in un'applicazione server è un'operazione che si verifica una volta (e una sola volta), all'avvio. Ad esempio, require utilizza effettivamentereadFileSync per caricare i moduli.

    Anche in questo caso, devi comunque fare attenzione perché molti I / O sincroni possono rallentare inutilmente il tempo di avvio del tuo server.


    Invece, è necessario utilizzare i metodi I / O asincroni.

Quindi se mettiamo insieme quei consigli, otteniamo qualcosa del genere:

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

E possiamo usarlo in questo modo:

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

Naturalmente, questo non tiene conto di casi limite come

  • Cosa succede se la cartella viene eliminata mentre il programma è in esecuzione? (supponendo che tu controlli che esiste una sola volta durante l'avvio)
  • Cosa succede se la cartella esiste già ma con le autorizzazioni sbagliate?

1
c'è un modo per evitare SyntaxError: i letterali ottali non sono ammessi in modalità rigorosa?
Whisher,

8
Scrivilo come un decimale. 0744 == 484.
josh3736,

3
Un'alternativa è quella di utilizzare un modulo che estende fs per avere questa funzionalità come github.com/jprichardson/node-fs-extra
Bret

questa bandiera "maschera" è ancora rilevante nel 2019? qual era lo scopo?
oldboy,

È la modalità file unix : le autorizzazioni di lettura / scrittura della directory.
josh3736,


34

Il mkdirmetodo ha la capacità di creare ricorsivamente qualsiasi directory in un percorso che non esiste e di ignorare quelle che lo fanno.

Dai documenti 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;
});

NOTA: devi prima importare il fsmodulo integrato.

Ora ecco un esempio un po 'più robusto che sfrutta i moduli ES nativi (con flag abilitato ed estensione .mjs), gestisce percorsi non root e tiene conto di percorsi completi:

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

Puoi usarlo come createDirectories('/components/widget/widget.js');.

E ovviamente, probabilmente vorrai essere più fantasioso usando le promesse con asincrono / attendi per sfruttare la creazione di file in un modo più leggibile in modo sincrono quando vengono create le directory; ma questo va oltre lo scopo della domanda.


1
Perché const __dirname = path.resolve (); e non utilizzare il __dirname incorporato?
TamusJRoyce,

29

Nel caso qualcuno fosse interessato alla versione a una riga. :)

//or in typescript: import * as fs from 'fs';
const fs = require('fs');
!fs.existsSync(dir) && fs.mkdirSync(dir);

Presunta 1 linea non in realtà 1 linea.
Dev web ibrido

20

Puoi semplicemente usare mkdire rilevare l'errore se la cartella esiste.
Questo è asincrono (quindi la migliore pratica) e sicuro.

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

(Facoltativamente aggiungi un secondo argomento con la modalità.)


Altri pensieri:

  1. È possibile utilizzare quindi o attendere utilizzando Promisify nativo .

    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. Puoi creare il tuo metodo di promessa, qualcosa del tipo (non testato):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Per il controllo sincrono, è possibile utilizzare:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. Oppure puoi usare una libreria, i due più popolari

    • mkdirp (fa solo le cartelle)
    • fsextra (supersets fs, aggiunge molte cose utili)

1
per l'approccio promettente n. 1, è possibile riorganizzare il pescato. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
Cosa sarebbe bello

E usa !==invece di!=
Quentin Roy il

18

Con il pacchetto fs-extra puoi farlo con una sola riga :

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

const dir = '/tmp/this/path/does/not/exist';
fs.ensureDirSync(dir);

Una risposta così sottovalutata! fs-extra ha il bacame un must per me. Penso che sia un'abberrazione scrivere 10+ righe juste per verificare se esiste una cartella ...
538ROMEO

10

La soluzione migliore sarebbe quella di utilizzare il modulo npm chiamato node-fs-extra . Ha un metodo chiamato mkdirche crea la directory che hai citato. Se si fornisce un lungo percorso di directory, verranno create automaticamente le cartelle principali. Il modulo è un super set di moduli npm fs, quindi puoi usare tutte le funzioni fsanche se aggiungi questo modulo.


6
var dir = 'path/to/dir';
try {
  fs.mkdirSync(dir);
} catch(e) {
  if (e.code != 'EEXIST') throw e;
}

4
Per Node.js v7.4.0, la documentazione afferma che fs.exists()è obsoleto, ma fs.existsSync()non lo è. Potresti aggiungere un link a una risorsa dicendo che fs.existsSync()è ammortizzato?
francia,

1
Le risposte di solo codice non sono molto utili per gli utenti che verranno a questa domanda in futuro. Modifica la tua risposta per spiegare perché il tuo codice risolve il problema originale
yivi

3
@francis, hmm, stavo guardando Node.js v5, nodejs.org/docs/latest-v5.x/api/fs.html#fs_fs_existssync_path
Ping.Goblue

1
Grazie! Sembra che la funzione esistesse nella versione 0.12, è stata deprecata nelle versioni 4 e 5 e ripristinata nella versione 6 e 7 ... Tipo di una funzione zombi ...
francis

1
Sì, a quanto pare NON è deprecato ora a partire da 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");
    }

Questo può aiutarti :)


5

ENOENT: nessun file o directory

Soluzione

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 })

1
funziona, grazie
Aljohn Yamaro il

3

Vorrei aggiungere un refattore Promessa dattiloscritto della risposta di josh3736 .

Fa la stessa cosa e ha gli stessi casi limite, capita solo di usare Promesse, dattiloscritti dattiloscritti e funziona con "usa rigoroso".

// 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
                }
            });
    });
}

3

Con Nodo 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;
    }
  }
})();

2

È possibile utilizzare il comando fs.stat del file system del nodo per verificare l'esistenza di dir e fs.mkdir per creare una directory con callback oppure fs.mkdirSync per creare una directory senza callback, come in questo esempio:

//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);
        }
    });
};

1

Ecco una piccola funzione per creare ricorsivamente directory:

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
  }, '')
}

0

Usando async / await:

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

Dovrai promettere fs:

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

const fs = bluebird.promisifyAll(nodeFs);
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.