Как создать каталог, если он не существует с помощью Node.js?

657

Это правильный способ создать каталог, если он не существует. Он должен иметь полное разрешение для сценария и читаться другими.

var dir = __dirname + '/upload';
if (!path.existsSync(dir)) {
    fs.mkdirSync(dir, 0744);
}
Whisher
источник
1
Возможный дубликат Node.js для создания папки или использования существующей
Бенни Нойгебауэр,

Ответы:

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

if (!fs.existsSync(dir)){
    fs.mkdirSync(dir);
}
chovy
источник
28
Если вы выполняете эту операцию при загрузке или инициализации приложения, то можно блокировать выполнение, как если бы вы делали это асинхронно, то вы сделали бы то же самое. Если вы делаете каталог как повторяющуюся операцию, то это плохая практика, но, вероятно, она не вызовет проблем с производительностью, но, тем не менее, это плохая привычка. Используйте только для загрузки вашего приложения или других операций.
tsturzl
20
existsSync () не рекомендуется, существует () является хотя - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Чедвик
использование * Syncметодов обычно является
Макс
14
Использование методов синхронизации хорошо для локальных сценариев и т. Д., Очевидно, не очень хорошая идея для сервера.
Пирс
Если этот блок окружен setTimeout, он асинхронный ...................
Брайан Грейс
186

Нет, по нескольким причинам.

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

Конечно, это не учитывает крайние случаи, такие как

  • Что произойдет, если папка будет удалена во время работы вашей программы? (при условии, что вы проверяете, что он существует только один раз при запуске)
  • Что произойдет, если папка уже существует, но с неправильными разрешениями?
josh3736
источник
1
Есть ли способ избежать SyntaxError: Октальные литералы не допускаются в строгом режиме?
Whisher
8
Запишите это как десятичное число. 0744 == 484,
josh3736
3
Альтернативой является использование модуля, расширяющего fs, чтобы иметь эту функцию, такого как github.com/jprichardson/node-fs-extra
Брет,
этот флаг «маски» все еще актуален в 2019 году? какова была цель этого?
старик
Это режим файлов Unix - права на чтение и запись в каталоге.
Josh3736
44

Я нашел и модуль npm, который работает как шарм для этого. Он просто делает рекурсивный mkdir, когда это необходимо, например, "mkdir -p".

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

Тони Гамез
источник
34

mkdirМетод имеет возможность рекурсивно создавать любые каталоги в пути , которые не существуют, и игнорировать те , которые делают.

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

ПРИМЕЧАНИЕ. fsСначала вам нужно будет импортировать встроенный модуль.

Теперь вот немного более надежный пример, который использует собственные модули ES (с включенным флагом и расширением .mjs), обрабатывает пути без полномочий root и учитывает полные пути:

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 / await, чтобы использовать создание файлов в более читабельном синхронном виде при создании каталогов; но это выходит за рамки вопроса.

немного меньше
источник
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. Или вы можете использовать библиотеку, две самые популярные из которых

    • mkdirp (просто делает папки)
    • fsextra (суперсет fs, добавляет много полезного)
SamGoody
источник
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);
galki
источник
Такой недооцененный ответ! У 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;
}
Ping.Goblue
источник
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 })
Wasif
источник
1
это работает, спасибо
Алджон Ямаро
3

Я хотел бы добавить рефактор Typescript Promise к ответу josh3736 .

Он делает то же самое и имеет те же самые крайние случаи, он просто использует Promises, typedefs typefs и работает с «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

С Узлом 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
источник
2

Вы можете использовать команду файловой системы узла 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
  }, '')
}
MrBlenny
источник
0

Использование async / await:

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);
sdgfsdh
источник