Jak utworzyć katalog, jeśli nie istnieje za pomocą Node.js?


Odpowiedzi:


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

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

28
Jeśli wykonujesz tę operację przy uruchamianiu lub inicjowaniu aplikacji, możesz zablokować wykonywanie, ponieważ zrobiłbyś to samo, gdybyś zrobił to asynchronicznie. Jeśli tworzysz katalog jako operację cykliczną, to jego zła praktyka, ale prawdopodobnie nie spowoduje żadnych problemów z wydajnością, ale mimo wszystko jest to zły nawyk. Używaj tylko do uruchamiania aplikacji lub innych jednorazowych operacji.
tsturzl

20
existSync () nie jest przestarzałe, istnieje is () - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick

używanie * Syncmetod jest zwykle nie-nie: nie chcę blokować pętli zdarzeń
Max Heiber

14
Używanie metod synchronizacji jest odpowiednie dla lokalnych skryptów i oczywiście nie jest to dobry pomysł na serwer.
Molo

Jeśli ten blok jest otoczony przez setTimeout, jest asynchroniczny .....................
Bryan Grace

185

Nie, z wielu powodów.

  1. pathModuł nie ma exists/ existsSyncmetody. Jest w fsmodule. (Być może właśnie napisałeś literówkę w swoim pytaniu?)

  2. Dokumenty wyraźnie zniechęcają do korzystania exists.

    fs.exists()jest anachronizmem i istnieje tylko z powodów historycznych. Prawie nigdy nie powinno być powodu, aby używać go we własnym kodzie.

    W szczególności sprawdzenie, czy plik istnieje przed otwarciem, jest anty-wzorcem, który naraża cię na warunki wyścigu: inny proces może usunąć plik między wywołaniami do fs.exists()i fs.open(). Wystarczy otworzyć plik i obsłużyć błąd, gdy go nie ma.

    Ponieważ mówimy raczej o katalogu niż pliku, ta rada sugeruje, że powinieneś bezwarunkowo zadzwonić mkdiri zignorować EEXIST.

  3. Ogólnie powinieneś unikać * Syncmetod. Blokują, co oznacza, że ​​absolutnie nic innego w twoim programie nie może się zdarzyć podczas przechodzenia na dysk. Jest to bardzo kosztowna operacja, a czas potrzebny na złamanie podstawowego założenia pętli zdarzeń węzła.

    SyncMetody * są zwykle dobre w szybkich skryptach jednofunkcyjnych (tych, które wykonują jedną rzecz, a następnie wychodzą), ale prawie nigdy nie powinny być używane podczas pisania serwera: serwer nie będzie w stanie odpowiedzieć nikomu przez cały czas trwania żądań We / Wy. Jeśli wiele żądań klientów wymaga operacji we / wy, serwer bardzo szybko się zatrzyma.


    Jedynym momentem, w którym rozważę użycie * Syncmetod w aplikacji serwera, jest operacja, która dzieje się raz (i tylko raz) przy uruchomieniu. Na przykład require faktycznie używareadFileSync do ładowania modułów.

    Nawet wtedy musisz zachować ostrożność, ponieważ wiele synchronicznych operacji we / wy może niepotrzebnie spowalniać czas uruchamiania serwera.


    Zamiast tego należy użyć asynchronicznych metod we / wy.

Jeśli więc zbierzemy te porady, otrzymamy coś takiego:

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

I możemy użyć tego w następujący sposób:

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

Oczywiście nie uwzględnia to przypadków typu edge

  • Co się stanie, jeśli folder zostanie usunięty podczas działania programu? (zakładając, że sprawdzasz, czy istnieje tylko raz podczas uruchamiania)
  • Co się stanie, jeśli folder już istnieje, ale ma niewłaściwe uprawnienia?

1
Czy istnieje sposób na uniknięcie błędu SyntaxError: Literał ósemkowy nie jest dozwolony w trybie ścisłym?
Whisher,

8
Napisz to jako ułamek dziesiętny. 0744 == 484.
josh3736

3
Alternatywą jest użycie modułu, który rozszerza FS, aby mieć taką funkcjonalność, np. Github.com/jprichardson/node-fs-extra
Bret

czy ta flaga „maski” nadal jest istotna w 2019 r.? jaki był tego cel?
oldboy

Jest to tryb pliku unix - uprawnienia katalogu do odczytu / zapisu.
josh3736


33

mkdirMetoda ma zdolność do rekursywnie utworzyć żadnych katalogów w ścieżce, które nie istnieją, i ignorować te, które wykonują.

Z dokumentów 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;
});

UWAGA: Musisz zaimportować wbudowany fs Najpierw moduł.

Oto trochę bardziej solidny przykład, który wykorzystuje natywne moduły ES (z włączoną flagą i rozszerzeniem .mjs), obsługuje ścieżki inne niż root i uwzględnia pełne nazwy ścieżek:

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

Możesz go używać jak createDirectories('/components/widget/widget.js');.

I oczywiście, prawdopodobnie chciałbyś zyskać więcej fantazji, używając obietnic z asynchronizacją / czekaj, aby wykorzystać tworzenie plików w bardziej czytelny, synchroniczny sposób podczas tworzenia katalogów; ale to wykracza poza zakres pytania.


1
Dlaczego const __dirname = path.resolve (); i nie używać wbudowanego __dirname?
TamusJRoyce

29

Na wszelki wypadek zainteresowany wersją jednoliniową. :)

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

Rzekomy 1-liniowy, a nie 1-liniowy.
Hybrid web dev

20

Możesz po prostu użyć mkdiri złapać błąd, jeśli folder istnieje.
Jest to asynchroniczne (więc najlepsza praktyka) i bezpieczne.

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

(Opcjonalnie dodaj drugi argument do trybu).


Inne przemyślenia:

  1. Możesz wtedy użyć lub poczekać, używając natywnego 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. Możesz stworzyć własną metodę obietnicy, taką jak (niesprawdzona):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Do sprawdzania synchronicznego możesz użyć:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. Możesz też skorzystać z biblioteki, z której dwie są najbardziej popularne


1
w przypadku obiecującego podejścia nr 1 możesz ponownie ustawić haczyk. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
Co byłoby fajne

I użyj !==zamiast!=
Quentin Roy

18

Dzięki pakietowi fs-extra możesz to zrobić za pomocą jednej linijki :

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

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

Taka niedoceniana odpowiedź! fs-extra ma dla mnie konieczność. Myślę, że pisanie ponad 10 linii tylko w celu sprawdzenia, czy folder istnieje, jest
aberracją

10

Najlepszym rozwiązaniem byłoby użycie modułu npm o nazwie node-fs-extra . Ma metodę o nazwie, mkdirktóra tworzy wspomniany katalog. Jeśli podasz długą ścieżkę do katalogu, automatycznie utworzy ona foldery nadrzędne. Moduł jest super zestawem modułu npm fs, więc możesz korzystać ze wszystkich funkcji, fstakże po dodaniu tego modułu.


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

4
W przypadku Node.js w wersji 7.4.0 dokumentacja stwierdza, że fs.exists()jest przestarzała, ale fs.existsSync()nie jest. Czy możesz dodać link do zasobu informującego, że fs.existsSync()jest amortyzowany?
francis

1
Odpowiedzi zawierające tylko kod nie są zbyt pomocne dla użytkowników, którzy w przyszłości odpowiedzą na to pytanie. Edytuj swoją odpowiedź, aby wyjaśnić, dlaczego kod rozwiązuje pierwotny problem
yivi


1
Dzięki! Wygląda na to, że funkcja istniała w wersji 0.12, przestarzała w wersji 4 i 5 i została przywrócona w wersji 6 i 7 ... Rodzaj funkcji zombi ...
Franciszek

1
Tak, najwyraźniej NIE jest już przestarzałe od dnia 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");
    }

To może ci pomóc :)


5

ENOENT: brak takiego pliku lub katalogu

Rozwiązanie

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
to działa, dziękuję
Aljohn Yamaro

3

Chciałbym dodać refaktor Typescript Promise do odpowiedzi josh3736 .

Robi to samo i ma te same przypadki brzegowe, po prostu używa Obietnic, maszynopisów maszynopisowych i działa z „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
                }
            });
    });
}

3

W przypadku węzła 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

Możesz użyć węzła File System komendy fs.stat, aby sprawdzić, czy katalog istnieje, i fs.mkdir, aby utworzyć katalog z wywołaniem zwrotnym, lub fs.mkdirSync, aby utworzyć katalog bez wywołania zwrotnego, jak w tym przykładzie:

//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

Oto mała funkcja rekurencyjnego tworzenia katalogów:

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

Za pomocą asynchronizacji / oczekiwania:

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

Musisz obiecać fs:

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

const fs = bluebird.promisifyAll(nodeFs);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.