Синхронно проверяйте, существует ли файл / каталог в Node.js

1210

Как я могу синхронно проверить, используя node.js , существует ли файл или каталог?

Ragnis
источник
58
Синхронные операции отлично подходят для выполнения одноразовых операций с файлами / каталогами перед возвратом модуля. Например, начальная загрузка файла конфигурации.
jocull
1
@PaulDraper с тёплым кешем встречается не во всех случаях.
mikemaccana
12
Независимо от производительности, иногда вы просто хотите синхронизировать ее для разработчиков. Например, если вы используете Node для сценария обработки данных, который по своей конструкции должен блокировать, в этом случае async existsпросто добавляет ненужные обратные вызовы.
Кунок
3
Определенно +1 к заявлению Кунока. В остальной части моего кода я делаю код более сложным, только когда это узкое место, где скорость действительно имеет значение. Почему бы мне не применить этот принцип к чтению файлов? Во многих частях многих программ простота / удобочитаемость кода может быть важнее скорости выполнения. Если это узкое место, я буду использовать асинхронные методы, чтобы предотвратить дальнейшее выполнение кода. В противном случае ... Синхронизация это здорово. Не надо слепо ненавидеть синхронизацию.
BryanGrezeszak
3
Пожалуйста ... не «стоит отмечать», потому что пользователь явно спрашивает, как сделать это синхронно.
jClark

Ответы:

2241

Ответ на этот вопрос изменился за эти годы. Текущий ответ здесь в верхней части, а затем различными ответами на протяжении многих лет в хронологическом порядке:

Текущий ответ

Вы можете использовать fs.existsSync():

const fs = require("fs"); // Or `import fs from "fs";` with ESM
if (fs.existsSync(path)) {
    // Do something
}

Это было объявлено устаревшим в течение нескольких лет, но больше не является. Из документов:

Обратите внимание, что fs.exists()это устарело, но fs.existsSync()это не так. (Параметр обратного вызова для fs.exists()принятия параметров, которые несовместимы с другими обратными вызовами Node.js. fs.existsSync()Не использует обратный вызов.)

Вы специально просили синхронную проверку, но если вы можете вместо этого использовать асинхронную проверку (обычно лучше всего с вводом / выводом), используйте, fs.promises.accessесли вы используете asyncфункции или fs.access(поскольку existsустарело ), если нет:

В asyncфункции:

try {
    await fs.promises.access("somefile");
    // The check succeeded
} catch (error) {
    // The check failed
}

Или с обратным вызовом:

fs.access("somefile", error => {
    if (!error) {
        // The check succeeded
    } else {
        // The check failed
    }
});

Исторические ответы

Вот исторические ответы в хронологическом порядке:

  • Оригинальный ответ с 2010 года
    ( stat/ statSyncили lstat/ lstatSync)
  • Обновление сентябрь 2012
    ( exists/ existsSync)
  • Обновление за февраль 2015 года
    (с учетом предстоящего устаревания exists/ existsSync, поэтому мы, вероятно, вернулись к stat/ statSyncили lstat/ lstatSync)
  • Обновление: декабрь 2015 г.
    (также есть fs.access(path, fs.F_OK, function(){})/ fs.accessSync(path, fs.F_OK), но обратите внимание, что если файл / каталог не существует, это ошибка; документы для fs.statрекомендации рекомендуется использовать, fs.accessесли вам нужно проверить наличие, не открывая)
  • Обновление декабря 2016 года
    fs.exists() по-прежнему не рекомендуется, но fs.existsSync()больше не устарело. Таким образом, вы можете безопасно использовать его сейчас.

Оригинальный ответ от 2010 года:

Вы можете использовать statSyncили lstatSync( ссылка на документацию ), которые дают вам fs.Statsобъект . Как правило, если доступна синхронная версия функции, она будет иметь то же имя, что и асинхронная версия Syncв конце. Такова statSyncсинхронная версия stat; lstatSyncэто синхронная версия lstatи т. д.

lstatSync сообщает вам, существует ли что-то, и если да, то является ли это файлом или каталогом (или в некоторых файловых системах, символической ссылкой, блочным устройством, символьным устройством и т. д.), например, если вам нужно знать, существует ли оно и существует ли оно каталог:

var fs = require('fs');
try {
    // Query the entry
    stats = fs.lstatSync('/the/path');

    // Is it a directory?
    if (stats.isDirectory()) {
        // Yes it is
    }
}
catch (e) {
    // ...
}

... и аналогично, если это файл, есть isFile; если это блочное устройство, есть и isBlockDeviceт. д. и т. д. Обратите внимание на try/catch; выдает ошибку, если запись вообще не существует.

Если вам все равно, что это за запись , и вы хотите знать только, существует ли она, вы можете использовать path.existsSync(или с последним, fs.existsSync), как отмечено пользователем 618408 :

var path = require('path');
if (path.existsSync("/the/path")) { // or fs.existsSync
    // ...
}

Это не требует, try/catchно не дает вам никакой информации о том, что это за штука, просто она там есть. path.existsSyncдавно устарел.


Примечание: вы прямо спросили, как проверять синхронно , поэтому я использовал xyzSyncверсии функций выше. Но там, где это возможно, с вводом / выводом лучше избегать синхронных вызовов. Вызовы в подсистему ввода / вывода занимают значительное время с точки зрения процессора. Обратите внимание, как просто позвонить, lstatа не lstatSync:

// Is it a directory?
lstat('/the/path', function(err, stats) {
    if (!err && stats.isDirectory()) {
        // Yes it is
    }
});

Но если вам нужна синхронная версия, она есть.

Обновление сентябрь 2012

Нижеприведенный ответ пару лет назад сейчас немного устарел. Текущий способ - использовать fs.existsSyncсинхронную проверку существования файла / каталога (или, конечно, fs.existsасинхронную проверку), а не pathверсии ниже.

Пример:

var fs = require('fs');

if (fs.existsSync(path)) {
    // Do something
}

// Or

fs.exists(path, function(exists) {
    if (exists) {
        // Do something
    }
});

Обновление февраль 2015

И вот мы находимся в 2015 году, и теперь в Документах Node говорится, что fs.existsSyncfs.exists) «будет объявлено устаревшим». (Потому что люди из Node думают, что глупо проверять, существует ли что-то, прежде чем открывать это, что есть; но это не единственная причина для проверки, существует ли что-то!)

Поэтому мы, вероятно, вернулись к различным statметодам ... До тех пор, пока это не изменится, конечно.

Обновление декабрь 2015

Не знаю, как давно там, но есть и fs.access(path, fs.F_OK, ...)/fs.accessSync(path, fs.F_OK) . И, по крайней мере, по состоянию на октябрь 2016 года в fs.statдокументации рекомендуется использовать fs.accessпроверку существования ( «fs.access() Рекомендуется проверить, существует ли файл, не манипулируя им впоследствии ». ). Но обратите внимание, что недоступность доступа считается ошибкой , поэтому, вероятно, будет лучше, если вы ожидаете, что файл будет доступен:

var fs = require('fs');

try {
    fs.accessSync(path, fs.F_OK);
    // Do something
} catch (e) {
    // It isn't accessible
}

// Or

fs.access(path, fs.F_OK, function(err) {
    if (!err) {
        // Do something
    } else {
        // It isn't accessible
    }
});

Обновление декабря 2016

Вы можете использовать fs.existsSync():

if (fs.existsSync(path)) {
    // Do something
}

Это было объявлено устаревшим в течение нескольких лет, но больше не является. Из документов:

Обратите внимание, что fs.exists()это устарело, но fs.existsSync()это не так. (Параметр обратного вызова для fs.exists()принятия параметров, которые несовместимы с другими обратными вызовами Node.js. fs.existsSync()Не использует обратный вызов.)

TJ Crowder
источник
7
path.exists и path.existsSync были объявлены устаревшими в пользу fs.exists и fs.existsSync
Дрю
15
«Узловые люди думают, что глупо проверять, существует ли что-то перед тем, как его открыть». Почему глупо проверять, существует ли файл?
Петр Хуртак
32
@PetrHurtak: Это не всегда (потому что есть много причин для проверки существования), но если вы собираетесь открыть файл, лучше всего выполнить openвызов и обработать исключение или что-то еще, если файл не был нашел. В конце концов, реальный мир хаотичен: если вы сначала проверите, и он там, это не значит, что он все еще будет там, когда вы попытаетесь открыть его; если вы проверите сначала, а его там нет, это не значит, что его не будет и через мгновение. Подобные сроки кажутся крайними случаями, но они возникают постоянно . Так что, если вы собираетесь открыть, нет смысла проверять сначала.
TJ Crowder
13
И тут я подумал, что это анти-паттерн - использовать ошибки для потока управления: ссылка
argyle
4
@jeromeyers: Вы могли бы, но Ionică уже сделала это для вас (см. комментарий выше ). :-)
TJ Crowder
124

Глядя на источник, есть синхронная версия path.exists- path.existsSync. Похоже, это было пропущено в документах.

Обновить:

path.existsи path.existsSyncтеперь устарели . Пожалуйста, используйте fs.existsиfs.existsSync .

Обновление 2016:

fs.exists и fs.existsSyncтакже устарели . Вместо этого используйте fs.stat () или fs.access () .

Обновление 2019:

использовать fs.existsSync. Это не рекомендуется. https://nodejs.org/api/fs.html#fs_fs_existssync_path

Джефф
источник
1
path.existsSync (p) находится в узле 0.4.10 nodejs.org/docs/v0.4.10/api/path.html
Пол Бустериен,
21
На самом деле более свежий ответ: path.existsSync устарел. Это сейчас называется fs.existsSync.
Оливье Лалонд
9
Теперь в документах говорится, что fs.exists устарела. nodejs.org/api/fs.html#fs_fs_existssync_path
Грег Хорнби
Я написал небольшую библиотеку, чтобы заменить старую existsфункцию:is-there
Ionică Bizau
6
Current Docs (версия ~ 9) помечена fs.existsкак устаревшая, а fs.existsSyncнет!
Кунок
57

Используя рекомендуемые в настоящее время (по состоянию на 2015 г.) API-интерфейсы (согласно документации по Node), я делаю вот что:

var fs = require('fs');

function fileExists(filePath)
{
    try
    {
        return fs.statSync(filePath).isFile();
    }
    catch (err)
    {
        return false;
    }
}

В ответ на проблему EPERM, поднятую @broadband в комментариях, это поднимает хороший вопрос. fileExists (), вероятно, не очень хороший способ думать об этом во многих случаях, потому что fileExists () не может действительно обещать логическое возвращение. Возможно, вы сможете точно определить, существует ли файл или не существует, но вы также можете получить ошибку прав доступа. Ошибка прав доступа не обязательно означает, что файл существует, поскольку у вас может не быть разрешения на каталог, содержащий файл, который вы проверяете. И, конечно, есть вероятность того, что при проверке существования файла вы можете столкнуться с какой-то другой ошибкой.

Таким образом, мой код выше действительно делает FileExistAndDoIHaveAccessToIt (), но ваш вопрос может быть связан с doFileNotExistAndCouldICreateIt (), который будет представлять собой совершенно другую логику (которая должна учитывать ошибку EPERM, помимо прочего).

В то время как ответ fs.existsSync непосредственно отвечает на вопрос, заданный здесь, часто это не то, что вы хотите (вы не просто хотите знать, существует ли «что-то» на пути, вам, вероятно, важно знать, есть ли «вещь»). существует файл или каталог).

Суть в том, что если вы проверяете, существует ли файл, вы, вероятно, делаете это, потому что намереваетесь предпринять какое-то действие в зависимости от результата, и эта логика (проверка и / или последующее действие) должна соответствовать идее. что вещь, найденная по этому пути, может быть файлом или каталогом, и что вы можете столкнуться с EPERM или другими ошибками в процессе проверки.

BobDickinson
источник
4
Хорошо, я добавил || isDirectory () для проверки файлов / папок. var stats = fs.statSync (filePath); вернуть stats.isFile () || stats.isDirectory ();
Боб
4
Если у программы нет прав доступа к файлу, она все равно возвращает false, даже если файл существует, т.е. удалите все ригты из файла chmod ugo-rwx file.txt или в Windows, щелкните правой кнопкой мыши ... Сообщение об исключении: Exception fs.statSync (./ f.txt): Ошибка: EPERM: операция не разрешена, stat 'X: \ f.txt'. Так что этот случай не охватывается верхним кодом.
широкополосный
2
Вау, JS иногда отсталый. Вы уверены, что в 97% случаев вы будете использовать файл, но у вас не будет простой file.exists()утилиты для 3%, и вместо этого вынудите нас обернуть это в попытку? Стань настоящим ... Сука дня.
expelledboy
20

Другое обновление

Нуждаясь в ответе на этот вопрос сам, я посмотрел документацию по узлу, кажется, вам не следует использовать fs.exists, вместо этого используйте fs.open и используйте выведенную ошибку, чтобы определить, не существует ли файл:

из документов:

fs.exists () является анахронизмом и существует только по историческим причинам. Почти никогда не должно быть причин использовать его в своем собственном коде.

В частности, проверка, существует ли файл перед открытием, является антишаблоном, который делает вас уязвимым для условий гонки: другой процесс может удалить файл между вызовами fs.exists () и fs.open (). Просто откройте файл и обработайте ошибку, когда ее там нет.

http://nodejs.org/api/fs.html#fs_fs_exists_path_callback

Melbourne2991
источник
1
есть ли способ сделать это с помощью openSync, а не с открытым
Грег Хорнби
1
@GregHornby Я полагаю, что с openSync должно работать то же самое
Melbourne2991
2
Для тех, кто еще нужен exists и existsSyncсоздан is-there.
Ионика Бизэ
6
Это оскорбление беспокоит меня. Открытие файла только для того, чтобы посмотреть, была ли выдана ошибка или нет, кажется пустой тратой ресурсов, когда все, что нужно, это знание о существовании файла.
Джош Хансен
11

Я использую функцию ниже, чтобы проверить, существует ли файл. Ловит и другие исключения. Так что в случае проблем с правами, например, chmod ugo-rwx filenameили в Windows Right Click -> Properties -> Security -> Advanced -> Permission entries: empty list ..функция возвращает исключение, как и должно быть. Файл существует, но у нас нет прав на доступ к нему. Было бы неправильно игнорировать подобные исключения.

function fileExists(path) {

  try  {
    return fs.statSync(path).isFile();
  }
  catch (e) {

    if (e.code == 'ENOENT') { // no such file or directory. File really does not exist
      console.log("File does not exist.");
      return false;
    }

    console.log("Exception fs.statSync (" + path + "): " + e);
    throw e; // something else went wrong, we don't have rights, ...
  }
}

Вывод исключений, документация об ошибках nodejs в случае, если файл не существует:

{
  [Error: ENOENT: no such file or directory, stat 'X:\\delsdfsdf.txt']
  errno: -4058,
  code: 'ENOENT',
  syscall: 'stat',
  path: 'X:\\delsdfsdf.txt'
}

Исключение в случае, если у нас нет прав на файл, но существует:

{
  [Error: EPERM: operation not permitted, stat 'X:\file.txt']
  errno: -4048,
  code: 'EPERM',
  syscall: 'stat',
  path: 'X:\\file.txt'
}
широкополосный
источник
2
Действительно, это один из немногих актуальных ответов, так как узел отказался от последних 37 способов сделать это
1mike12
Бах, ты побил меня этим. Я мог бы сэкономить время, если бы прочитал это.
jgmjgm
5

fs.exists () устарела, не используйте его https://nodejs.org/api/fs.html#fs_fs_exists_path_callback

Вы можете реализовать основной метод nodejs, используемый здесь: https://github.com/nodejs/node-v0.x-archive/blob/master/lib/module.js#L86

function statPath(path) {
  try {
    return fs.statSync(path);
  } catch (ex) {}
  return false;
}

это вернет объект статистики, после того как вы получите объект статистики, вы можете попробовать

var exist = statPath('/path/to/your/file.js');
if(exist && exist.isFile()) {
  // do something
}
gsalgadotoledo
источник
4

Некоторые ответы здесь говорят, что fs.existsи fs.existsSyncоба устарели. Согласно документам, это больше не правда. Только fs.existsсейчас вычеркнуто:

Обратите внимание, что fs.exists () устарела, а fs.existsSync () - нет. (Параметр обратного вызова для fs.exists () принимает параметры, которые несовместимы с другими обратными вызовами Node.js. Fs.existsSync () не использует обратный вызов.)

Таким образом, вы можете безопасно использовать fs.existsSync (), чтобы синхронно проверить, существует ли файл.

jstice4all
источник
3

pathМодуль не обеспечивает синхронную версию , path.existsтак что вы должны обмануть вокруг с fsмодулем.

Самая быстрая вещь, которую я могу себе представить, это использование, fs.realpathSyncкоторое выдаст ошибку, которую вы должны отловить, поэтому вам нужно создать свою собственную функцию-обертку с помощью try / catch.

Иво Ветцель
источник
1

Использование тестов fileSystem (fs) вызовет объекты ошибок, которые затем нужно будет заключить в оператор try / catch. Сэкономьте немного усилий и используйте функцию, представленную в ветке 0.4.x.

var path = require('path');

var dirs = ['one', 'two', 'three'];

dirs.map(function(dir) {
  path.exists(dir, function(exists) {
    var message = (exists) ? dir + ': is a directory' : dir + ': is not a directory';
    console.log(message);
  });
});

источник
2
Path.exists теперь находится под fs, поэтому это fs.exists (path, callback)
Тодд Моисей
0

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

Я использую узел для линейной автоматизации, поэтому я решил поделиться функцией, которую я использую для проверки существования файла.

var fs = require("fs");

function exists(path){
    //Remember file access time will slow your program.
    try{
        fs.accessSync(path);
    } catch (err){
        return false;
    }
    return true;
}
Grallen
источник
0

обновленный asnwer для тех людей, «правильно» указывающих на то, что он не дает прямого ответа на вопрос, больше предлагает альтернативный вариант.

Синхронизация решения:

fs.existsSync('filePath')также см. документы здесь .

Возвращает true, если путь существует, иначе false.

Решение Async Promise

В асинхронном контексте вы можете просто написать асинхронную версию в методе синхронизации с использованием awaitключевого слова. Вы можете просто превратить асинхронный метод обратного вызова в обещание, подобное этому:

function fileExists(path){
  return new Promise((resolve, fail) => fs.access(path, fs.constants.F_OK, 
    (err, result) => err ? fail(err) : resolve(result))
  //F_OK checks if file is visible, is default does no need to be specified.

}

async function doSomething() {
  var exists = await fileExists('filePath');
  if(exists){ 
    console.log('file exists');
  }
}

документы по доступу ().

Джоэл Харкс
источник
1
ОП хочет синхронное решение
vdegenne
Вы должны обновить свой код доfunction asyncFileExists(path) { //F_OK checks if file is visible, is default does no need to be specified. return new Promise(function (res, rej) { fs.access( path, fs.constants.F_OK, function (err) { err ? rej(err) : res(true); }, ); }); }
Pery Mimon
0

Скорее всего, если вы хотите знать, существует ли файл, вы планируете требовать его, если он существует.

function getFile(path){
    try{
        return require(path);
    }catch(e){
        return false;
    }
}
SwiftNinjaPro
источник
-1

Вот простое решение для этого:

var fs = require('fs')
function getFileRealPath(s){
    try {return fs.realpathSync(s);} catch(e){return false;}
}

Применение:

  • Работает как для каталогов и файлов
  • Если элемент существует, он возвращает путь к файлу или каталогу
  • Если элемент не существует, он возвращает ложь

Пример:

var realPath,pathToCheck='<your_dir_or_file>'
if( (realPath=getFileRealPath(pathToCheck)) === false){
    console.log('file/dir not found: '+pathToCheck);
} else {
    console.log('file/dir exists: '+realPath);
}

Убедитесь, что вы используете оператор ===, чтобы проверить, равен ли return false. Нет логической причины, по которой fs.realpathSync () вернул бы false при надлежащих рабочих условиях, поэтому я думаю, что это должно работать на 100%.

Я бы предпочел увидеть решение, которое не генерирует ошибку и приводит к снижению производительности. С точки зрения API fs.exists () кажется наиболее элегантным решением.

Тимоти К. Куинн
источник
1
@ Дэн, спасибо. Я удалил усеченный текст. Я не могу вспомнить, что это была за записка. Если придет ко мне, я добавлю заметки.
Тимоти К. Куинн
1
Np. Я удаляю свой комментарий.
Дан Даскалеску
-2

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

Допустим, мы попробуем это с чем-то, что не существует:

$ node -e 'require("fs").stat("god",err=>console.log(err))'
{ Error: ENOENT: no such file or directory, stat 'god' errno: -2, code: 'ENOENT', syscall: 'stat', path: 'god' }

Давайте попробуем это с тем, что существует, но у нас нет доступа к:

$ mkdir -p fsm/appendage && sudo chmod 0 fsm
$ node -e 'require("fs").stat("fsm/appendage",err=>console.log(err))'
{ Error: EACCES: permission denied, stat 'access/access' errno: -13, code: 'EACCES', syscall: 'stat', path: 'fsm/appendage' }

По крайней мере, вы захотите:

let dir_exists = async path => {
    let stat;
    try {
       stat = await (new Promise(
           (resolve, reject) => require('fs').stat(path,
               (err, result) => err ? reject(err) : resolve(result))
       ));
    }
    catch(e) {
        if(e.code === 'ENOENT') return false;
        throw e;
    }

    if(!stat.isDirectory())
        throw new Error('Not a directory.');

    return true;
};

Вопрос не ясен, если вы действительно хотите, чтобы он был синхронным или вы хотите, чтобы он был записан так, как если бы он был синхронным. В этом примере используется await / async, поэтому он записывается только синхронно, но работает асинхронно.

Это означает, что вы должны называть это так на верхнем уровне:

(async () => {
    try {
        console.log(await dir_exists('god'));
        console.log(await dir_exists('fsm/appendage'));
    }
    catch(e) {
        console.log(e);
    }
})();

Альтернативой является использование .then и .catch для обещания, возвращенного из асинхронного вызова, если вам это нужно дальше.

Если вы хотите проверить, существует ли что-то, рекомендуется также убедиться, что это правильный тип вещи, такой как каталог или файл. Это включено в пример. Если не разрешено быть символической ссылкой, вы должны использовать lstat вместо stat, так как stat будет автоматически проходить по ссылкам.

Вы можете заменить всю асинхронность для синхронизации кода здесь и использовать вместо этого statSync. Однако следует ожидать, что как только асинхронность и ожидание станут повсеместно поддерживаемыми, вызовы Sync со временем станут избыточными и будут обесцениваться (в противном случае вам придется определять их везде и по цепочке, как в случае асинхронизации, что делает его действительно бессмысленным).

jgmjgm
источник
1
Оригинальный вопрос не указывает на это. Я также демонстрирую, как делать вещи однозначно. Многие ответы могут вызвать ошибки из-за отсутствия ясности. Люди часто хотят программировать вещи так, чтобы они выглядели синхронно, но не обязательно хотят синхронного выполнения. statSync отличается от кода, который я продемонстрировал. Либо сообщения о том, что на самом деле требуется, неоднозначны, поэтому вы только навязываете свои личные интерпретации. Если вы найдете ответ, который вам не понятен, может быть, лучше просто спросить в комментариях или в личном кабинете, чтобы выяснить, какие изменения необходимы.
августа
1
Если вы хотите, вы также можете украсть мой пример кода, назовите его соответствующим образом, поместите на github, добавьте в npm, и тогда ответом будет только одна строка / ссылка: D.
августа
Для краткости код короток, но вы можете представить предложение по редактированию, включающее &&! IsFile или проверку символических ссылок и т. Д. (Опять же, хотя в вопросе никогда не указывается, что это именно то, что они хотят). Как я уже указывал, мой ответ удовлетворяет одной интерпретации вопроса и не делает того же, что и ваше однострочное предложение.
августа