Як створити каталог, якщо він не існує за допомогою Node.js?


655

Це правильний спосіб створення каталогу, якщо він не існує. Він повинен мати повний дозвіл на сценарій і читати його іншими.

var dir = __dirname + '/upload';
if (!path.existsSync(dir)) {
    fs.mkdirSync(dir, 0744);
}

Відповіді:


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

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

28
Якщо ви виконуєте цю операцію під час завантаження програми чи ініціалізації, то її штраф блокує виконання, як ви б зробили те саме, якби це робилося для асинхронізації. Якщо ви створюєте каталог як повторювану операцію, то його погана практика, але, ймовірно, не спричинить жодних проблем з продуктивністю, але все-таки поганий хабіт. Використовуйте лише для завантаження програми або в інший спосіб.
tsturzl

20
съществуваSync () не застаріло, існує (), хоча - nodejs.org/api/fs.html#fs_fs_existssync_path
Іван Чадвік

використання Syncметодів * зазвичай - ні-ні: не хочуть блокувати цикл подій
Макс Хайбер

14
Використовувати методи синхронізації, це добре для локальних сценаріїв, і такий сервер, очевидно, не є гарною ідеєю для сервера.
Пристань

Якщо цей блок оточений setTimeout, це асинхронізація .....................
Брайан Грейс,

185

Ні, з кількох причин.

  1. pathМодуль не маєexists / existsSyncметоду. Це в fsмодулі. (Можливо, ви щойно зробили помилку на друку у своєму питанні?)

  2. Документи явно заважають вам використовувати exists.

    fs.exists()є анахронізмом і існує лише з історичних причин. Майже ніколи не повинно бути причин використовувати його у власному коді.

    Зокрема, перевірка наявності файлу перед відкриттям - це анти-шаблон, який залишає вас вразливими до перегонових умов: інший процес може видалити файл між викликами до fs.exists() та fs.open(). Просто відкрийте файл і обробіть помилку, коли його немає.

    Оскільки ми говоримо про каталог, а не про файл, ця порада означає, що вам слід просто беззастережно зателефонувати mkdir та ігнорувати EEXIST.

  3. Загалом, вам слід уникати *Sync методів . Вони блокуються, а це означає, що нічого іншого у вашій програмі не може статися під час переходу на диск. Це дуже дорога операція, і час, який потрібно, порушує основне припущення про цикл подій вузла.

    Методи *, Syncяк правило, є чудовими у швидких сценаріях, що мають одну ціль (ті, що роблять одне, а потім виходять), але майже ніколи не повинні використовуватися під час написання сервера: ваш сервер не зможе відповісти нікому протягом усієї тривалості запитів вводу / виводу. Якщо для декількох запитів клієнтів потрібні операції вводу / виводу, ваш сервер дуже швидко перемелиться.


    Єдиний раз, коли я б розглядав можливість використання * Syncметодів у серверній програмі, це операція, що відбувається один раз (і лише один раз), при запуску. Наприклад, require фактично використовуєreadFileSync для завантаження модулів.

    Навіть тоді ви все ще повинні бути обережними, оскільки багато синхронних вводу-виводу можуть зайво уповільнити час запуску вашого сервера.


    Натомість слід використовувати асинхронні методи вводу / виводу.

Тож якщо ми з’єднаємо ці поради, отримаємо щось подібне:

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

І ми можемо використовувати це так:

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

Звичайно, це не враховує таких крайових випадків

  • Що станеться, якщо папка буде видалена під час роботи вашої програми? (припускаючи, що ви перевіряєте, чи існує лише один раз під час запуску)
  • Що станеться, якщо папка вже існує, але з неправильними дозволами?

1
чи є спосіб уникнути SyntaxError: Восьмі літерали не дозволені в суворому режимі?
Вішер

8
Запишіть його як десятковий. 0744 == 484.
josh3736

3
Альтернативою є використання модуля, який розширює fs, щоб мати таку функціональність, як github.com/jprichardson/node-fs-extra
Bret,

чи цей актуальний прапор "маски" все ще актуальний у 2019 році? яка була мета цього?
oldboy

Це режим файлу unix - дозволи на читання / запис каталогів.
josh3736


33

mkdirМетод має можливість рекурсивно створити будь-які каталоги в дорозі , які не існують, і ігнорувати ті , які роблять.

З вузла 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;
});

ПРИМІТКА. Вам потрібно буде імпортувати вбудований модуль fs . Спочатку модуль.

Тепер ось трохи більш надійний приклад, який використовує нативні модулі ES (з увімкненим прапором та розширенням .mjs), обробляє некорінні шляхи та облікові записи для повних імен:

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

Ви можете використовувати його як createDirectories('/components/widget/widget.js');.

І звичайно, ви, мабуть, захочете отримати більше фантазії, використовуючи обіцянки з async / очікуючи, щоб скористатися створенням файлів більш читаним синхронним виглядом, коли створюються каталоги; але це виходить за межі питання.


1
Чому const __dirname = path.resolve (); і не використовувати вбудоване __dirname?
TamusJRoyce

29

Про всяк випадок, коли хтось зацікавлений у версії однієї лінії. :)

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

Передбачуваний 1-лайнер насправді не 1 рядок.
Гібридна веб-розробка

20

Ви можете просто скористатися mkdirпомилкою, якщо папка існує.
Це асинхроніка (так найкраща практика) і безпечна.

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

(За бажанням додайте другий аргумент із режимом.)


Інші думки:

  1. Ви можете користуватися тоді або чекати, використовуючи нативні пропіси .

    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. Ви можете зробити власний метод обіцянки, на кшталт (неперевірений):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Для синхронної перевірки ви можете використовувати:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. Або ви можете використовувати бібліотеку, дві найпопулярніші


1
для перспективного підходу №1 ви могли б перемовити улов. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
Що було б круто

І використовувати !==замість!=
Квентін Рой

18

За допомогою пакета fs-extra ви можете це зробити за допомогою одноклайничного :

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

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

Така занижена відповідь! fs-extra має для мене обов'язковий обов'язок. Я думаю, що це скасування запису 10+ рядків, щоб перевірити, чи існує папка ...
538ROMEO

10

Найкращим рішенням буде використання модуля npm під назвою node-fs-extra . Він має метод, mkdirякий називається, який створює згаданий вами каталог. Якщо надати довгий шлях до каталогу, він створить батьківські папки автоматично. Модуль - це супер набір npm-модуля fs, тому ви можете використовувати всі функції, fsякщо ви додасте цей модуль.


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

4
Для Node.js v7.4.0 в документації зазначено, що fs.exists()застаріло, але fs.existsSync()це не так. Чи можете ви додати посилання на ресурс, який говорить про fs.existsSync()амортизацію?
Францис

1
Відповіді, що стосуються лише коду, не дуже допомагають користувачам, які в майбутньому прийдуть до цього питання. Відредагуйте свою відповідь, щоб пояснити, чому ваш код вирішує оригінальну проблему
yivi

3
@francis, хм, я дивився на Node.js v5, nodejs.org/docs/latest-v5.x/api/fs.html#fs_fs_existssync_path
Ping.Goblue

1
Дякую! Здається, що функція існувала у версії 0.12, застаріла у версії 4 та 5 та була відновлена ​​у версіях 6 та 7 ... Вид функції зомбі ...
Францис

1
Так, мабуть, це зараз НЕ застаріло станом на Apr 2018: nodejs.org/api/fs.html#fs_fs_existssync_path
LeOn - Хан Лі

5
    var filessystem = require('fs');
    var dir = './path/subpath/';

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

Це може вам допомогти :)


5

ENOENT: такого файлу чи каталогу немає

Рішення

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
це працює, дякую
Альон Ямаро

3

Я хотів би додати рефактор "Обіцяння набору тексту" відповіді josh3736 .

Це те ж саме і має однакові крайові випадки, просто трапляється використовувати Promises, typecript typedefs та працює з "строго використовувати".

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

З вузлом 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

Ви можете скористатися командою файлової системи ns.fs.stat, щоб перевірити, чи існує dir, і fs.mkdir для створення каталогу з зворотним викликом, або fs.mkdirSync для створення каталогу без зворотного виклику, наприклад, наприклад:

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

Ось невелика функція для рекурсивного створення каталогів:

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

Використання асинхронізації / очікування:

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

Вам потрібно буде зазначити fs:

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

const fs = bluebird.promisifyAll(nodeFs);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.