Перевірте синхронно, чи існує файл / каталог у Node.js


1207

Як я можу синхронно перевірити, використовуючи node.js , чи існує файл чи каталог?


57
Синхронні операції чудово підходять для виконання одноразових операцій з файлами / каталогами перед поверненням модуля. Наприклад, завантажувальний файл конфігурації.
jocull

1
@PaulDraper з теплим кешем не відповідає дійсності у всіх випадках.
mikemaccana

11
Незалежно від продуктивності, іноді просто потрібно запустити його синхронізовано для досвіду розробника. Наприклад, якщо ви використовуєте Node для скрипту обробки даних, який за задумом повинен блокувати, в цьому випадку async existsпросто додає непотрібні зворотні виклики.
Кунок

2
Однозначно +1 до заяви Кунока. У решті свого коду я ускладнюю код лише тоді, коли це вузьке місце, де швидкість дійсно має значення. Чому я б не застосував цей принцип до файлу читання? У багатьох частинах багатьох програм простота / читабельність коду може бути важливішою, ніж швидкість виконання. Якщо це вузьке місце, я використовую методи асинхронізації, щоб не зупиняти подальше виконання коду. Інакше ... синхронізація чудова. Не ненавиджу сліпо синхронізацію.
BryanGrezeszak

3
Будь ласка ... не варто "зауважувати", оскільки користувач чітко запитує, як це зробити синхронно.
jClark

Відповіді:


2238

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

Поточна відповідь

Ви можете використовувати 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) "буде застарілим". (Тому що люди з Вузла думають, що німе, щоб перевірити, чи щось існує, перш ніж відкрити, що це таке; але це не єдина причина перевірки, чи щось існує!)

Тож ми, мабуть, повертаємось до різних 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()Не використовує зворотного дзвінка.)


7
path.exists та path.existsSync були вимкнені на користь fs.exists та fs.existsSync
Дрю

15
"Люди, що знаходяться у вузлі, вважають, що це нерозумно перевірити, чи існує щось, перш ніж відкрити його, що це; Чому дурно перевіряти, чи існує файл?
Петро Хуртак

32
@PetrHurtak: Це не завжди (тому що існує велика кількість причин для перевірки існування), але якщо ви збираєтесь відкрити файл, то краще просто надіслати openвиклик і обробити виняток або все, якщо файл не був знайдено. Зрештою, реальний світ хаотичний: якщо ви спочатку перевірите, чи він там, це не означає, що він все одно буде там, коли ви намагаєтесь його відкрити; якщо ви перевірите спочатку, а його там немає, це не означає, що мить його не буде. Зубчасте тощо , здається , як крайні випадки, але вони приходять все час . Тож якщо ви збираєтеся відкривати, немає сенсу перевіряти спочатку.
TJ Crowder

13
І тут я подумав, що це анти-шаблон, щоб використовувати помилки для управління потоком: link
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ă Bizău

6
Документи поточного струму (версія ~ 9), позначені лише fs.existsяк застарілі, поки fs.existsSyncнемає!
Кунок

57

Використовуючи рекомендовані на даний момент API (станом на 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 (), але ваше запитання може бутиFileNotExistAndCouldICreateIt (), що було б зовсім іншою логікою (для цього потрібно було б пояснити помилку EPERM).

Хоча відповідь fs.existsSync стосується прямо заданого тут питання, це часто не буде таким, яким ви хочете (ви не просто хочете знати, чи існує щось на шляху, ви, мабуть, хвилюєтесь, чи є "річ" який існує - це файл або каталог).

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


4
Добре, я додав || isDirectory (), щоб зробити його перевірки файлів / папок. var stats = fs.statSync (filePath); повернути stats.isFile () || stats.isDirectory ();
боб

4
Якщо програма не має прав на доступ до файлу, вона все одно повертає помилкову інформацію, навіть якщо файл існує, тобто видаліть усі версії з файлу chmod ugo-rwx file.txt або в Windows клацніть правою кнопкою миші ... Повідомлення про виняток: Виняток fs.statSync (./ f.txt): Помилка: EPERM: операція заборонена, stat 'X: \ f.txt'. Тому цей випадок не охоплюється верхнім кодом.
широкосмуговий

2
Нічого, JS іноді відстає. Так що впевнено, що 97% часу ви будете використовувати файл, але не маючи простий file.exists()утиліти для 3%, а натомість змушуєте нас обернути це у спробі ловити? Отримайте справжню ... Сука дня.
висланий

20

Ще одне оновлення

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

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

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

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

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


1
є спосіб зробити це з openSync, а не з відкритим
Грег Хорнбі,

1
@GregHornby Я думаю, що це має працювати так само і з openSync
Мельбурн2991,

2
Для тих, хто ще потребує, exists і existsSyncя створив is-there.
Ionică Bizău

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

11

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

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
}

4

Деякі відповіді тут говорять про це fs.existsі fs.existsSyncобидва застарілі. Згідно з документами, це більше не відповідає дійсності. Тільки fs.existsв deprected Зараз:

Зауважте, що fs.exists () застаріле, але fs.existsSync () - ні. (Параметр зворотного виклику до fs.exists () приймає параметри, несумісні з іншими зворотними викликами Node.js. Fs.existsSync () не використовує зворотний виклик.)

Таким чином, ви можете сміливо використовувати fs.existsSync () для синхронної перевірки наявності файлу.


3

pathМодуль не забезпечує синхронну версію , path.existsтак що ви повинні обдурити навколо зfs модулем.

Найшвидше, що я можу собі уявити, - це використання, fs.realpathSyncяке призведе до помилки, яку вам доведеться зловити, тому вам потрібно зробити власну функцію обгортки за допомогою спробувати.


1

Використання тестів fileSystem (fs) призведе до появи об'єктів помилок, які вам потім потрібно буде загорнути у операцію спробувати. Економте собі певні зусилля та скористайтеся функцією введення у відділенні 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 (шлях, зворотний виклик)
Todd Moses

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

0

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

Рішення для синхронізації:

fs.existsSync('filePath')також дивіться документи тут .

Повертає істину, якщо шлях існує, інакше помилково.

Рішення Async Promise

У контексті асинхронізації ви можете просто записати версію async методом синхронізації за допомогою awaitключового слова. Ви можете просто перетворити метод зворотного виклику async у таке обіцянку:

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
ОП хоче синхронного рішення
вдеген

ви повинні оновити свій код до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;
    }
}

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

Переконайтеся, що ви використовуєте оператор === для перевірки, чи повернення дорівнює хибності. Немає жодної логічної причини, щоб fs.realpathSync () повертав помилкове значення при належних умовах праці, тому я думаю, що це має працювати на 100%.

Я хотів би бачити рішення, яке не генерує помилки та результативного результату. З точки зору API, fs.exists () видається найелегантнішим рішенням.


1
@Dan, спасибі Я видалив усічений текст. Я не можу згадати, якою була записка. Якщо він прийде до мене, я додам нотатки.
Тимофі К. Квін

1
Np. Я видаляю свій коментар.
Дан Даскалеску

-2

З відповідей видно, що офіційної підтримки API для цього немає (як у прямому та явному перевірці). У багатьох відповідях йдеться про використання 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 на обіцянку, повернуту з виклику async, якщо вам це потрібно далі.

Якщо ви хочете перевірити, чи щось існує, то це добра практика, щоб також переконатися, що це правильний тип речей, таких як каталог або файл. Це включено в приклад. Якщо заборонено бути символьним посиланням, ви повинні використовувати lstat замість stat, оскільки stat автоматично переходитиме посиланнями.

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


1
Первісне питання цього не визначає. Я також демонструю, як робити речі однозначно. Багато відповідей можуть викликати помилки через недостатню чіткість. Люди часто хочуть запрограмувати речі, щоб вони виглядали синхронними, але не обов'язково хочуть синхронного виконання. statSync - це не те саме, що я продемонстрував. Будь-які відомості про те, що бажано насправді, неоднозначні, тому ви нав'язуєте лише свої особисті трактування. Якщо ви знайдете відповідь, яку ви не розумієте, може бути краще просто попросити в коментарях чи прем'єр-міністра визначити, які зміни потрібні.
jgmjgm

1
Якщо ви хочете, ви можете також вкрасти мій зразок коду, назвати його відповідним чином, покласти його на github, додати його до npm, і тоді відповідь буде лише одним рядком / посиланням: D.
jgmjgm

Код короткий для прикладу, але ви можете надіслати пропозицію щодо редагування, щоб включити &&! IsFile або чек на символьні посилання тощо (знову ж таки, в цьому питанні ніколи прямо не зазначено, що саме вони хочуть). Як я вже зазначав, моя відповідь задовольняє одне тлумачення питання і не робить те саме, що робить одна пропозиція в одному рядку.
jgmjgm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.