Node.js создайте папку или используйте существующую

186

Я уже прочитал документацию по Node.js и, если я что-то пропустил, он не сообщает, какие параметры содержатся в определенных операциях, в частности fs.mkdir(). Как видно из документации, это не очень много.

В настоящее время у меня есть этот код, который пытается создать папку или использовать вместо нее существующую:

fs.mkdir(path,function(e){
    if(!e || (e && e.code === 'EEXIST')){
        //do something with contents
    } else {
        //debug
        console.log(e);
    }
});

Но мне интересно, это правильный способ сделать это? Является ли проверка кода EEXISTправильным способом узнать, что папка уже существует? Я знаю, что могу сделать fs.stat()до создания каталога, но это уже будет два попадания в файловую систему.

Во-вторых, есть ли полная или, по крайней мере, более подробная документация по Node.js, которая содержит подробную информацию о том, что содержат объекты ошибок, что означают параметры и т. Д.

Джозеф
источник
31
Небольшой придира, но избавься от e &&. Если !eне eполучится , значит, ты правдива.
Я ненавижу Ленивых

Ответы:

236

Хороший способ сделать это - использовать модуль mkdirp .

$ npm install mkdirp

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

var mkdirp = require('mkdirp');
mkdirp('/tmp/some/path/foo', function(err) { 

    // path exists unless there was an error

});
Теему Иконен
источник
3
Мне кажется, что правильный ответ (читай «без добавления зависимостей») был бы ответом ниже @Raugaral, используя fs.exists(Sync).
Рикардо Педрони
@meawoppl, это «makedirectory» p. «Р» неизвестно.
Андрей
4
@RicardoPedroni Правильный способ - использовать модуль. Модули, как правило, от всего сердца пытаются решить одну проблему и часто поддерживаются. Вы можете легко обновить их с помощью npm. Кроме того, вам следует избегать использования fs.exists [Sync], поскольку его использование подразумевает условия гонки.
1j01
16
@ 1j01 Я не верю, что правильно использовать модуль, если платформа изначально поддерживает эту операцию. Это дорога к хаосу. Я должен согласиться, что есть лучшие ответы с технической точки зрения.
с ..
2
@ 1j01 Кроме того, использование операций синхронизации подразумевает условия гонки, поскольку их использование является для них разрешением.
с ..
193

Изменить: Поскольку этот ответ очень популярен, я обновил его, чтобы отразить современные практики.

Узел> = 10

Новая { recursive: true }опция Node's fsтеперь позволяет это изначально. Эта опция имитирует поведение UNIX mkdir -p. Он рекурсивно удостоверится, что каждая часть пути существует, и не выдаст ошибку, если какая-либо из них сделает.

(Примечание: он может по-прежнему выдавать ошибки, такие как EPERMили EACCESS, поэтому лучше обернуть его вtry {} catch (e) {} если ваша реализация подвержена этому.)

Синхронная версия.

fs.mkdirSync(dirpath, { recursive: true })

Асинхронная версия

await fs.promises.mkdir(dirpath, { recursive: true })

Старые версии Node

Используя try {} catch (err) {} , вы можете достичь этого очень изящно, не встречая условия гонки.

Чтобы предотвратить потерю времени между проверкой существования и созданием каталога, мы просто пытаемся создать его прямо вверх и игнорировать ошибку, если она есть. EEXIST (каталог уже существует).

Если ошибка не EEXIST, однако, мы должны выбросить ошибку, потому что мы могли бы иметь дело с чем-то вроде EPERMилиEACCES

function ensureDirSync (dirpath) {
  try {
    return fs.mkdirSync(dirpath)
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }
}

Для mkdir -p-like рекурсивного поведения, например ./a/b/c, вы должны были бы назвать его на каждой части dirpath, например ./a, ./a/b,.a/b/c

Кристоф Маруа
источник
var fs = Npm.require ('fs'); var dir = process.env.PWD + '/ files / users /' + this.userId + '/'; try {fs.mkdirSync (dir); } catch (e) {if (e.code! = 'EEXIST') throw e; }
Аарон
Я попробовал ваш код, создайте js-скрипт, который использует создание каталога следующим образом: mkdirpSync (path.join (__dirname, 'first', 'second', 'third', 'ololol', 'works')); Но получил эту ошибку: $ node 1.js fs.js: 747 return binding.mkdir (pathModule._makeLong (path), ^ Ошибка: EPERM, операция не разрешена, C: \ при ошибке (собственная) в Object.fs. mkdirSync (fs.js: 747: 18) в mkdirpSync (C: \ Users \ MAXIM \ Desktop \ test \ 1.js: 15: 8) в Object. <анонимный> (C: \ Users \ MAXIM \ Desktop \ test \ 1.js: 19: 1) ... Не могли бы вы предположить, что может быть не так? Используется на окнах, очевидно :)
Alendorff
EPERM выглядит как проблема с разрешением, поэтому сценарий в любом случае нарушил бы исполнение
Кристоф Маруа
Я думаю, что было бы лучше: var mkdirpSync = function (dirpath) {var parts = dirpath.split (path.sep); for (var i = 1; i <= parts.length; i ++) {try {fs.mkdirSync (path.join.apply (null, parts.slice (0, i))); } catch (error) {if (error.code! = 'EEXIST') {throw error; }}}}
Маниш
1
предупреждение: это не работает, если ваш путь начинается с /
acemtp
62

Если вы хотите быстрый и грязный лайнер, используйте это:

fs.existsSync("directory") || fs.mkdirSync("directory");
marekventur
источник
1
fs.existsустарело: nodejs.org/api/fs.html#fs_fs_exists_path_callback
adius,
7
fs.existsSync (...) не считается устаревшим, поэтому этот ответ кажется нормальным.
Дэн Хейвуд
Берегись! Не будет работать для "dir / foo / bar", т.е. отсутствует функция флага mkdir -p
Карл Покус
Это также имеет состояние гонки
Evert
26

Документы по node.js для по fs.mkdirсуществу относятся к справочной странице по Linux для mkdir(2). Это означает, что EEXISTтакже будет указано, если путь существует, но не является каталогом, который создает неудобный угловой случай, если вы идете по этому пути.

Возможно, вам лучше позвонить, fs.statчтобы узнать, существует ли путь и является ли он каталогом за один вызов. Для (что я предполагаю, что) нормальный случай, когда каталог уже существует, это всего лишь один удар по файловой системе.

Эти fsметоды модуля являются тонкими оболочками вокруг собственных API-интерфейсов C, поэтому вы должны проверить подробности в справочных страницах, на которые есть ссылки в документации по node.js.

JohnnyHK
источник
19
Вызов statдо того, mkdirкак это может привести к состоянию гонки, имейте это в виду.
Роджер Липскомб
24

Вы можете использовать это:

if(!fs.existsSync("directory")){
    fs.mkdirSync("directory", 0766, function(err){
        if(err){
            console.log(err);
            // echo the result back
            response.send("ERROR! Can't make the directory! \n");
        }
    });
}
Raugaral
источник
1
-1. Я не верю, что это работает, statSyncвыдаст ошибку, если сущность вообще не существует , что приведет к сбою кода. Вы должны обернуть это в try/catchблоке.
Крис Фостер
2
Извините я не прав. изменить "statSync" на "existSync"
Raugaral
5
Согласно nodejs.org/api/fs.html#fs_fs_mkdirsync_path_mode вариант синхронизации mkdir не принимает обратный вызов
danwellman
1
Согласно nodejs.org/api/fs.html#fs_fs_existssync_path , fs.existsSync()и fs.exists()будет объявлено устаревшим.
pau.moreno
7

Я предлагаю решение без модулей (накапливать модули никогда не рекомендуется для удобства обслуживания, особенно для небольших функций, которые можно записать в несколько строк ...):

ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ :

В v10.12.0 NodeJS реализует рекурсивные опции:

// Create recursive folder
fs.mkdir('my/new/folder/create', { recursive: true }, (err) => { if (err) throw err; });

ОБНОВИТЬ :

// Get modules node
const fs   = require('fs');
const path = require('path');

// Create 
function mkdirpath(dirPath)
{
    if(!fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK))
    {
        try
        {
            fs.mkdirSync(dirPath);
        }
        catch(e)
        {
            mkdirpath(path.dirname(dirPath));
            mkdirpath(dirPath);
        }
    }
}

// Create folder path
mkdirpath('my/new/folder/create');
Liberateur
источник
fs.exists()устарела в узле v9. используйте fs.access()вместо этого. (возвращает, undefinedесли файл существует; в противном случае выдает ошибку ENOENT)
chharvey
Без пакета npm все работает. Это ценный код. Спасибо
Картик Шридхарен
Этот лучше подходит для создания папки по существующему длинному пути;) Спасибо, мужик.
Цунг Го,
1
Как насчет fs.mkdirSync('my/new/folder/create', {recursive: true})?
saitho
Спасибо ! Я обновляю свой пост, чтобы помочь другим. Узел 10.12.0 был слишком свежим.
Освободитель
4

Вы также можете использовать fs-extra , который предоставляет много часто используемых файловых операций.

Образец кода:

var fs = require('fs-extra')

fs.mkdirs('/tmp/some/long/path/that/prob/doesnt/exist', function (err) {
  if (err) return console.error(err)
  console.log("success!")
})

fs.mkdirsSync('/tmp/another/path')

документы здесь: https://github.com/jprichardson/node-fs-extra#mkdirsdir-callback

Geng Jiawen
источник
4

Вот код ES6, который я использую для создания каталога (когда он не существует):

const fs = require('fs');
const path = require('path');

function createDirectory(directoryPath) {
  const directory = path.normalize(directoryPath);

  return new Promise((resolve, reject) => {
    fs.stat(directory, (error) => {
      if (error) {
        if (error.code === 'ENOENT') {
          fs.mkdir(directory, (error) => {
            if (error) {
              reject(error);
            } else {
              resolve(directory);
            }
          });
        } else {
          reject(error);
        }
      } else {
        resolve(directory);
      }
    });
  });
}

const directoryPath = `${__dirname}/test`;

createDirectory(directoryPath).then((path) => {
  console.log(`Successfully created directory: '${path}'`);
}).catch((error) => {
  console.log(`Problem creating directory: ${error.message}`)
});

Примечание:

  • В начале createDirectoryфункции я нормализую путь, чтобы гарантировать, что тип разделителя пути операционной системы будет использоваться последовательно (например, это превратится C:\directory/testв C:\directory\test(при работе в Windows)
  • fs.existsявляется устаревшим , поэтому я использую , fs.statчтобы проверить , если каталог уже существует
  • Если каталог не существует, код ошибки будет ENOENT( E rror NO ENT ry)
  • Сам каталог будет создан с помощью fs.mkdir
  • Я предпочитаю асинхронную функцию по fs.mkdirсравнению с ее блокирующим аналогом, fs.mkdirSyncи из-за переноса Promiseбудет гарантировано, что путь к каталогу будет возвращен только после успешного создания каталога
Бенни Нойгебауэр
источник
Спасибо за чистое решение, которое не включает ненужные модули. Это отлично сработало для меня. Я хотел бы, чтобы было больше ответов, как это!
Кен Лион
3

На мой взгляд, вам лучше не считать хиты файловой системы при написании кода на Javascript. Тем не менее, (1) statи mkdirи (2) mkdirи проверьте (или отбросьте) код ошибки, оба способа являются правильными способами сделать то, что вы хотите.

Чул-Вунг Ян
источник
-1, я не понимаю, как проверка или сброс могут быть правильными способами сделать это. Это в значительной степени не ответ.
Мэтт Болл
Это хороший способ создать каталог или использовать существующий. Я не понимаю, почему ты не видишь. Проверка кода ошибки является вежливым, в то время как сброс кода ошибки - только хороший. ты не согласен?
Чул-Вунг Ян
1
Может быть, это проблема языкового барьера, но я читаю это как просто не отвечая на вопрос, который задает ОП.
Мэтт Болл
Я понимаю, в чем ваша точка зрения. Тем не менее, я считаю, что есть много способов сделать все правильно. Спасибо.
Чул-Вунг Ян
2

создать каталог динамических имен для каждого пользователя ... используйте этот код

***suppose email contain user mail address***

var filessystem = require('fs');
var dir = './public/uploads/'+email;

if (!filessystem.existsSync(dir)){
  filessystem.mkdirSync(dir);

}else
{
    console.log("Directory already exist");
}
Adiii
источник
1

Вы можете сделать все это с модулем файловой системы.

const
  fs = require('fs'),
  dirPath = `path/to/dir`

// Check if directory exists.
fs.access(dirPath, fs.constants.F_OK, (err)=>{
  if (err){
    // Create directory if directory does not exist.
    fs.mkdir(dirPath, {recursive:true}, (err)=>{
      if (err) console.log(`Error creating directory: ${err}`)
      else console.log('Directory created successfully.')
    })
  }
  // Directory now exists.
})

Вам действительно даже не нужно проверять, существует ли каталог. Следующий код также гарантирует, что каталог уже существует или создан.

const
  fs = require('fs'),
  dirPath = `path/to/dir`

// Create directory if directory does not exist.
fs.mkdir(dirPath, {recursive:true}, (err)=>{
  if (err) console.log(`Error creating directory: ${err}`)
  // Directory now exists.
})
OldBoy
источник
0

Ответ Раугарала, но с функциональностью -p. Некрасиво, но работает

function mkdirp(dir) {
    let dirs = dir.split(/\\/).filter(asdf => !asdf.match(/^\s*$/))
    let fullpath = ''

    // Production directory will begin \\, test is on my local drive.
    if (dirs[0].match(/C:/i)) {
        fullpath = dirs[0] + '\\'
    }
    else {
        fullpath = '\\\\' + dirs[0] + '\\'
    }

    // Start from root directory + 1, build out one level at a time.
    dirs.slice(1).map(asdf => {
        fullpath += asdf + '\\'
        if (!fs.existsSync(fullpath)) {
            fs.mkdirSync(fullpath)
        }
    })
}//mkdirp
user8675309
источник
0

Так же, как более новая альтернатива ответу Teemu Ikonen , который очень прост и легко читаем, это использование ensureDirметода fs-extraпакета.

Он может быть использован не только как явная замена встроенному fsмодулю, но также обладает множеством других функций в дополнение к функциональности fsпакета.

ensureDirМетод, как следует из названия, гарантирует , что каталог существует. Если структура каталогов не существует, она создается. Как mkdir -p. Не только конечная папка, но и весь путь, если он еще не создан.

приведенная выше asyncверсия является его версией. У этого также есть синхронный метод, чтобы выполнить это в форме ensureDirSyncметода.

Rai
источник
0

Ответ @ Liberateur выше не работал для меня (Node v8.10.0). Небольшая модификация сделала свое дело, но я не уверен, что это правильный путь. Пожалуйста, предложите.

// Get modules node
const fs   = require('fs');
const path = require('path');

// Create
function mkdirpath(dirPath)
{
    try {
        fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
    }
    catch(err) {
        try
        {
            fs.mkdirSync(dirPath);
        }
        catch(e)
        {
            mkdirpath(path.dirname(dirPath));
            mkdirpath(dirPath);
        }
    }
}

// Create folder path
mkdirpath('my/new/folder/create');
Nik
источник