Verifique de forma síncrona se o arquivo / diretório existe no Node.js

1209

Como posso verificar de forma síncrona, usando o node.js , se existe um arquivo ou diretório?

Ragnis
fonte
58
As operações síncronas são ótimas para executar operações únicas de arquivo / diretório antes de retornar um módulo. Por exemplo, inicializando um arquivo de configuração.
jocull
1
O @PaulDraper com um cache quente não é verdadeiro em todos os casos.
Mikemaccana 17/08/2017
12
Independentemente das performances, às vezes você deseja executá-lo de forma sincronizada para a experiência do desenvolvedor. Por exemplo, se você estiver usando o Nó para um script de processamento de dados que, por design, deveria estar bloqueando, nesse caso, o async existsadicionará apenas retornos de chamada desnecessários.
Kunok
3
Definitivamente +1 na declaração de Kunok. No restante do meu código, só o torno mais complexo quando é um gargalo em que a velocidade realmente importa. Por que eu não aplicaria esse princípio à leitura de arquivos? Em muitas partes de muitos programas, a simplicidade / legibilidade do código pode ser mais importante que a velocidade de execução. Se for uma área de gargalo, usarei métodos assíncronos para impedir a interrupção da execução do código. Caso contrário ... a sincronização é ótima. Não odeie cegamente a sincronização.
BryanGrezeszak
3
Por favor ... não é "digno de nota" porque o usuário pergunta explicitamente como fazê-lo de forma síncrona.
JClark

Respostas:

2241

A resposta a esta pergunta mudou ao longo dos anos. A resposta atual está aqui no topo, seguida pelas várias respostas ao longo dos anos em ordem cronológica:

Resposta Atual

Você pode usar fs.existsSync():

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

Foi preterido por vários anos, mas não é mais. Dos documentos:

Observe que fs.exists()está obsoleto, mas fs.existsSync()não está. (O parâmetro de retorno de chamada para fs.exists()aceita parâmetros inconsistentes com outros retornos de chamada do Node.js. fs.existsSync()não usa um retorno de chamada.)

Você solicitou especificamente uma verificação síncrona , mas se você pode usar uma verificação assíncrona (geralmente melhor com E / S), use fs.promises.accessse estiver usando asyncfunções ou fs.access(já que existsfoi preterido ) se não:

Em uma asyncfunção:

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

Ou com um retorno de chamada:

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

Respostas Históricas

Aqui estão as respostas históricas em ordem cronológica:

  • Resposta original de 2010
    ( stat/ statSyncou lstat/ lstatSync)
  • Atualização de setembro de 2012
    ( exists/ existsSync)
  • Atualização em fevereiro de 2015
    (observando a obsolescência iminente de exists/ existsSync, portanto, provavelmente estamos de volta a stat/ statSyncou lstat/ lstatSync)
  • Atualização em dezembro de 2015
    (também há fs.access(path, fs.F_OK, function(){})/ fs.accessSync(path, fs.F_OK), mas observe que, se o arquivo / diretório não existir, será um erro; documentos para fs.statrecomendar o uso fs.accessse você precisar verificar a existência sem abrir)
  • A atualização de dezembro de 2016
    fs.exists() ainda está obsoleta, mas fs.existsSync()não está mais obsoleta. Então você pode usá-lo com segurança agora.

Resposta original de 2010:

Você pode usar statSyncor lstatSync( link do docs ), que fornece um fs.Statsobjeto . Em geral, se uma versão síncrona de uma função estiver disponível, ela terá o mesmo nome da versão assíncrona Syncno final. Assim statSyncé a versão síncrona de stat; lstatSyncé a versão síncrona de lstatetc.

lstatSync informa se existe algo e, em caso afirmativo, se é um arquivo ou um diretório (ou em alguns sistemas de arquivos, um link simbólico, um dispositivo de bloco, um dispositivo de caracteres etc.), por exemplo, se você precisa saber se existe ou não um diretório:

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) {
    // ...
}

... e da mesma forma, se é um arquivo, existe isFile; se é um dispositivo de bloco, há isBlockDevice, etc., etc. Observe o try/catch; gera um erro se a entrada não existir.

Se você não se importa que a entrada é e só quero saber se ele existe, você pode usar path.existsSync(ou com o mais recente, fs.existsSync) como observado por user618408 :

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

Não requer um, try/catchmas não fornece informações sobre o que é a coisa, apenas que está lá. path.existsSyncfoi preterido há muito tempo.


Nota lateral: Você perguntou expressamente como verificar de forma síncrona ; portanto, usei as xyzSyncversões das funções acima. Mas sempre que possível, com E / S, é realmente melhor evitar chamadas síncronas. As chamadas para o subsistema de E / S levam um tempo significativo do ponto de vista da CPU. Observe como é fácil ligar em lstatvez de lstatSync:

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

Mas se você precisar da versão síncrona, ela estará lá.

Atualização setembro de 2012

A resposta abaixo de alguns anos atrás agora está um pouco desatualizada. A maneira atual é usar fs.existsSyncpara fazer uma verificação síncrona da existência de arquivo / diretório (ou, é claro, fs.existsde uma verificação assíncrona), em vez das pathversões abaixo.

Exemplo:

var fs = require('fs');

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

// Or

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

Atualização de fevereiro de 2015

E aqui estamos em 2015 e os documentos do Node agora dizem que fs.existsSync(e fs.exists) "será preterido". (Como o pessoal do Nó acha que é estúpido verificar se existe algo antes de abri-lo, qual é; mas esse não é o único motivo para verificar se existe algo!)

Então, provavelmente estamos de volta aos vários statmétodos ... Até / a menos que isso mude novamente, é claro.

Atualização dezembro de 2015

Não sei há quanto tempo está lá, mas também há fs.access(path, fs.F_OK, ...)/fs.accessSync(path, fs.F_OK) . E, pelo menos a partir de outubro de 2016, a fs.statdocumentação recomenda o uso fs.accessde verificações de existência ( "Para verificar se existe um arquivo sem manipulá-lo posteriormente, fs.access()é recomendável." ). Mas observe que o acesso não disponível é considerado um erro ; portanto, provavelmente seria melhor se você espera que o arquivo seja acessível:

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

Atualização dezembro de 2016

Você pode usar fs.existsSync():

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

Foi preterido por vários anos, mas não é mais. Dos documentos:

Observe que fs.exists()está obsoleto, mas fs.existsSync()não está. (O parâmetro de retorno de chamada para fs.exists()aceita parâmetros inconsistentes com outros retornos de chamada do Node.js. fs.existsSync()não usa um retorno de chamada.)

TJ Crowder
fonte
7
path.exists e path.existsSync foram preteridos em favor de fs.exists e fs.existsSync
Drew
15
"O pessoal do nó acha idiota verificar se existe algo antes de abri-lo, o que é;" Por que é idiota verificar se o arquivo existe?
Petr Hurtak
32
@PetrHurtak: nem sempre (porque existem muitas razões para verificar a existência), mas se você deseja abrir o arquivo, é melhor emitir a openchamada e manipular a exceção ou o que quer que seja, se o arquivo não estiver disponível. encontrado. Afinal, o mundo real é caótico: se você verificar primeiro e ele estiver lá, isso não significa que ainda estará lá quando você tentar abri-lo; se você verificar primeiro e ele não estiver lá, isso não significa que não estará lá um momento depois. Cronometrar coisas assim parecem casos extremos, mas surgem o tempo todo . Portanto, se você for abrir, não adianta verificar primeiro.
TJ Crowder
13
E aqui eu pensei que era um anti-padrão de erros de uso para controle de fluxo: ligação
argyle
4
@jeromeyers: Você poderia, mas o Ionică já fez isso por você (veja o comentário acima ). :-)
TJ Crowder
124

Olhando a fonte, há uma versão síncrona de path.exists- path.existsSync. Parece que foi esquecido nos documentos.

Atualizar:

path.existse path.existsSyncagora estão obsoletos . Por favor, use fs.existsefs.existsSync .

Atualização 2016:

fs.exists e fs.existsSynctambém foram preteridos . Use fs.stat () ou fs.access () .

Atualização 2019:

use fs.existsSync. Não está obsoleto. https://nodejs.org/api/fs.html#fs_fs_existssync_path

Jeff
fonte
1
path.existsSync (p) está nos 0.4.10 docs nodejs.org/docs/v0.4.10/api/path.html
Paul Beusterien
21
Na verdade, uma resposta mais recente: path.existsSync está obsoleta. Agora é chamado fs.existsSync.
precisa
9
Agora, os documentos estão dizendo que fs.exists será descontinuado. nodejs.org/api/fs.html#fs_fs_existssync_path
Greg Hornby
Escrevi uma pequena biblioteca para substituir a existsfunção antiga :is-there
Ionică Bizău 19/10/2015
6
documentos atuais (versão ~ 9) rotulados apenas fs.existscomo obsoletos, enquanto fs.existsSyncnão são!
Kunok
57

Usando as APIs atualmente recomendadas (a partir de 2015) (de acordo com os documentos do Node), é isso que eu faço:

var fs = require('fs');

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

Em resposta à questão EPERM levantada pela @broadband nos comentários, isso traz um bom argumento. fileExists () provavelmente não é uma boa maneira de pensar sobre isso em muitos casos, porque fileExists () não pode realmente prometer um retorno booleano. Você pode determinar definitivamente se o arquivo existe ou não, mas também pode receber um erro de permissão. O erro de permissão não implica necessariamente que o arquivo existe, porque você pode não ter permissão para o diretório que contém o arquivo no qual está verificando. E, é claro, há a chance de você encontrar outro erro ao verificar a existência do arquivo.

Portanto, meu código acima é realmente doesFileExistAndDoIHaveAccessToIt (), mas sua pergunta pode ser doesFileNotExistAndCouldICreateIt (), que seria uma lógica completamente diferente (que precisaria ser responsável por um erro EPERM, entre outras coisas).

Embora a resposta fs.existsSync atenda diretamente à pergunta feita aqui, isso geralmente não será o que você deseja (você não quer apenas saber se "algo" existe no caminho), provavelmente se preocupa se a "coisa" existe um arquivo ou diretório).

A conclusão é que, se você está verificando se existe um arquivo, provavelmente está fazendo isso porque pretende tomar alguma ação com base no resultado, e essa lógica (a verificação e / ou a ação subsequente) deve acomodar a ideia que algo encontrado nesse caminho pode ser um arquivo ou um diretório e que você pode encontrar o EPERM ou outros erros no processo de verificação.

BobDickinson
fonte
4
Bom, eu adicionei || isDirectory () para torná-lo um verificador de arquivos / pastas. var stats = fs.statSync (filePath); retorna stats.isFile () || stats.isDirectory ();
bob
4
Se o programa não tiver direitos para acessar o arquivo, ele retornará falso, mesmo que exista, remova todos os rigts do arquivo chmod ugo-rwx file.txt ou clique com o botão direito do mouse em Windows ... Mensagem de exceção: Exceção fs.statSync (./ f.txt): Erro: EPERM: operação não permitida, stat 'X: \ f.txt'. Portanto, este caso não é coberto pelo código superior.
banda larga
2
Uau, JS às vezes é retardado. Então, 97% das vezes você usará o arquivo, mas não terá um file.exists()utilitário simples para os 3% e nos forçará a envolvê-lo em uma tentativa de captura? Caia na real ... Vadia do dia.
Expelledboy #
20

Outra atualização

Precisando de uma resposta para esta pergunta eu mesmo, procurei os documentos do nó, parece que você não deveria estar usando o fs.exists, em vez disso, use o fs.open e use o erro gerado para detectar se um arquivo não existe:

dos documentos:

fs.exists () é um anacronismo e existe apenas por razões históricas. Quase nunca deve haver um motivo para usá-lo em seu próprio código.

Em particular, verificar se um arquivo existe antes de abri-lo é um antipadrão que o deixa vulnerável às condições de corrida: outro processo pode remover o arquivo entre as chamadas para fs.exists () e fs.open (). Basta abrir o arquivo e manipular o erro quando ele não estiver lá.

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

Melbourne2991
fonte
1
existe uma maneira de fazê-lo com o openSync, em vez de aberto
Greg Hornby
1
@GregHornby Imagino que deva funcionar da mesma maneira com o openSync #
Melbourne2991
2
Para aqueles que ainda precisam exists e existsSynceu criei is-there.
Bizonic
6
Essa depreciação me incomoda. Abrir um arquivo apenas para ver se um erro é gerado ou não parece um desperdício de recursos quando tudo o que é necessário é conhecimento da existência do arquivo.
Josh Hansen
11

Eu uso a função abaixo para testar se o arquivo existe. Também pega outras exceções. Portanto, caso haja problemas de direitos, por exemplo, chmod ugo-rwx filenameou na Right Click -> Properties -> Security -> Advanced -> Permission entries: empty list ..função Windows , a exceção retornará como deveria. O arquivo existe, mas não temos direitos para acessá-lo. Seria errado ignorar esse tipo de exceção.

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, ...
  }
}

Saída de exceção, documentação de erros do nodejs no caso de o arquivo não existir:

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

Exceção no caso de não termos direitos sobre o arquivo, mas existe:

{
  [Error: EPERM: operation not permitted, stat 'X:\file.txt']
  errno: -4048,
  code: 'EPERM',
  syscall: 'stat',
  path: 'X:\\file.txt'
}
banda larga
fonte
2
Realmente como este, é uma das poucas respostas que é até data desde nó tem depreciado os últimos 37 maneiras de fazer isso
1mike12
Bah, você me venceu. Eu poderia ter economizado algum tempo se tivesse lido isso.
Jgmjgm 12/11
5

fs.exists () está obsoleto, não o use https://nodejs.org/api/fs.html#fs_fs_exists_path_callback

Você pode implementar a maneira principal do nodejs usada desta maneira: 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;
}

isso retornará o objeto de estatísticas e, depois de obter o objeto de estatísticas, você poderá tentar

var exist = statPath('/path/to/your/file.js');
if(exist && exist.isFile()) {
  // do something
}
gsalgadotoledo
fonte
4

Algumas respostas aqui dizem isso fs.existse fs.existsSyncestão obsoletas. De acordo com os documentos, isso não é mais verdade. Somente fs.existsestá depreciado agora:

Observe que fs.exists () está obsoleto, mas fs.existsSync () não está. (O parâmetro de retorno de chamada para fs.exists () aceita parâmetros inconsistentes com outros retornos de chamada do Node.js. fs.existsSync () não usa um retorno de chamada.)

Portanto, você pode usar com segurança o fs.existsSync () para verificar de forma síncrona se existe um arquivo.

jstice4all
fonte
3

O pathmódulo não fornece uma versão síncrona, path.existsentão você precisa contornar o fsmódulo.

A coisa mais rápida que posso imaginar é usar o fs.realpathSyncque lançará um erro que você deve capturar; portanto, você precisa fazer seu próprio wrapper funcionar com uma tentativa / captura.

Ivo Wetzel
fonte
1

O uso de testes fileSystem (fs) acionará objetos de erro, que você precisará agrupar em uma instrução try / catch. Economize um pouco de esforço e use um recurso apresentado na ramificação 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);
  });
});

fonte
2
Os path.exists está agora sob fs por isso é fs.exists (caminho, callback)
Todd Moses
0

Os documentos fs.stat()dizem para usar fs.access()se você não for manipular o arquivo. Não deu uma justificativa, pode ser mais rápido ou menos uso de memórias?

Eu uso o nó para automação linear, então pensei em compartilhar a função que utilizo para testar a existência de arquivos.

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
fonte
0

atualizado como resposta para as pessoas 'corretamente', indicando que não responde diretamente à pergunta, mais traz uma opção alternativa.

Solução de sincronização:

fs.existsSync('filePath')veja também documentos aqui .

Retorna true se o caminho existir, false caso contrário.

Solução Async Promise

Em um contexto assíncrono, você pode simplesmente escrever a versão assíncrona no método sync usando a awaitpalavra - chave Você pode simplesmente transformar o método de retorno de chamada assíncrona em uma promessa como esta:

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

os documentos sobre access ().

Joel Harkes
fonte
1
o OP quer uma solução síncrona
vdegenne
você deve atualizar seu código parafunction 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

As chances são de que, se você quiser saber se existe um arquivo, planeja solicitá-lo, se existir.

function getFile(path){
    try{
        return require(path);
    }catch(e){
        return false;
    }
}
SwiftNinjaPro
fonte
-1

Aqui está uma solução de wrapper simples para isso:

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

Uso:

  • Funciona para diretórios e arquivos
  • Se o item existir, ele retornará o caminho para o arquivo ou diretório
  • Se o item não existir, ele retornará false

Exemplo:

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

Certifique-se de usar o operador === para testar se o retorno é falso. Não há razão lógica para que fs.realpathSync () retorne false em condições de trabalho adequadas, portanto, acho que isso deve funcionar 100%.

Eu preferiria ver uma solução que não gera um erro e um resultado resultante. Da perspectiva da API, fs.exists () parece ser a solução mais elegante.

Timothy C. Quinn
fonte
1
@ Dan, obrigado. Eu removi o texto truncado. Não me lembro qual era a nota. Se vier, adicionarei notas.
Timothy C. Quinn
1
Np. Estou excluindo meu comentário.
Dan Dascalescu
-2

A partir das respostas, parece que não há suporte oficial da API para isso (como em uma verificação direta e explícita). Muitas das respostas dizem usar stat, no entanto, não são rigorosas. Não podemos assumir, por exemplo, que qualquer erro gerado por stat signifique que algo não existe.

Vamos dizer que tentamos com algo que não existe:

$ 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' }

Vamos tentar com algo que existe, mas que não temos acesso a:

$ 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' }

No mínimo, você deseja:

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

A questão não está clara se você realmente deseja que seja síncrono ou se apenas deseja que seja escrito como se fosse síncrono. Este exemplo usa aguardar / assíncrono para que ele seja gravado apenas de maneira sincronizada, mas seja executado de forma assíncrona.

Isso significa que você deve chamá-lo como tal no nível superior:

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

Uma alternativa é usar .then e .catch na promessa retornada da chamada assíncrona, se você precisar mais.

Se você deseja verificar se algo existe, é uma boa prática também garantir que seja o tipo certo de coisa, como um diretório ou arquivo. Isso está incluído no exemplo. Se não for permitido que seja um link simbólico, você deve usar lstat em vez de stat, pois o stat atravessará automaticamente os links.

Você pode substituir todo o código assíncrono para sincronizar aqui e usar o statSync. No entanto, espere que, uma vez que o assíncrono e o aguardar se tornem universalmente compatíveis, as chamadas de sincronização se tornarão redundantes para serem depreciadas (caso contrário, você teria que defini-las em todos os lugares e na cadeia, assim como com o assíncrono, tornando-o realmente inútil).

jgmjgm
fonte
1
A pergunta original não especifica isso. Também estou demonstrando como fazer as coisas sem ambiguidade. Muitas respostas podem induzir erros devido à falta de clareza. As pessoas geralmente querem programar as coisas para que pareçam síncronas, mas não necessariamente querem execução síncrona. statSync não é o mesmo que o código que demonstrei. As contas do que realmente é desejado são ambíguas, então você está apenas impondo suas interpretações pessoais. Se você encontrar uma resposta que não entende, talvez seja melhor perguntar nos comentários ou no MP para descobrir quais edições são necessárias.
Jgmjgm
1
Se você quiser, também pode roubar meu exemplo de código, nomeá-lo adequadamente, colocá-lo no github, adicioná-lo ao npm e a resposta será apenas uma linha / link: D.
Jgmjgm
O código é curto, por exemplo, mas você pode enviar uma sugestão de edição para incluir &&! IsFile ou uma verificação de links simbólicos, etc. Como já indiquei, minha resposta satisfaz uma interpretação da pergunta e não faz a mesma coisa que sua proposta de uma linha.
Jgmjgm # 10/18