Guia Node.js, parte 9: Trabalhando com o sistema de arquivos

Hoje, na nona parte da tradução do tutorial do Node.js., falaremos sobre como trabalhar com arquivos. Em particular, falaremos sobre módulos fs e path - sobre descritores de arquivos, sobre caminhos de arquivos, sobre como obter informações sobre arquivos, sobre como ler e escrever sobre eles, sobre como trabalhar com diretórios.




Trabalhando com descritores de arquivo no Node.js


Antes de poder interagir com os arquivos localizados no sistema de arquivos do seu servidor, é necessário obter um descritor de arquivo.

O descritor pode ser obtido usando o método assíncrono open() do módulo fs para abrir o arquivo:

 const fs = require('fs') fs.open('/Users/flavio/test.txt', 'r', (err, fd) => { //fd -    }) 

Observe o segundo parâmetro, r , usado ao chamar o método fs.open() . Este é um sinalizador que informa ao sistema que o arquivo está sendo aberto para leitura. Aqui estão mais alguns sinalizadores que são freqüentemente usados ​​ao trabalhar com este e alguns outros métodos:

  • r+ - abre o arquivo para leitura e gravação.
  • w+ - abra o arquivo para leitura e gravação, definindo o ponteiro do fluxo no início do arquivo. Se o arquivo não existir, ele será criado.
  • a - abra o arquivo para gravação, definindo o ponteiro do fluxo no final do arquivo. Se o arquivo não existir, ele será criado.
  • a+ - abra o arquivo para leitura e gravação, definindo o ponteiro do fluxo no final do arquivo. Se o arquivo não existir, ele será criado.

Os arquivos podem ser abertos usando o método síncrono fs.openSync() , que, em vez de fornecer um descritor de arquivo no retorno de chamada, o retorna:

 const fs = require('fs') try { const fd = fs.openSync('/Users/flavio/test.txt', 'r') } catch (err) { console.error(err) } 

Após receber o descritor usando qualquer um dos métodos acima, você pode executar as operações necessárias com ele.

Dados do arquivo


Cada arquivo possui um conjunto de dados associado; você pode examinar esses dados usando o Node.js. Em particular, isso pode ser feito usando o método stat() do módulo fs .

Esse método é chamado, passando o caminho para o arquivo e, depois que o Node.js recebe as informações necessárias sobre o arquivo, ele chama o retorno de chamada passado para o método stat() . Aqui está o que parece:

 const fs = require('fs') fs.stat('/Users/flavio/test.txt', (err, stats) => { if (err) {   console.error(err)   return } //      `stats` }) 

O Node.js tem a capacidade de recuperar informações de arquivo de forma síncrona. Com essa abordagem, o encadeamento principal é bloqueado até que as propriedades do arquivo sejam obtidas:

 const fs = require('fs') try { const stats = fs.statSync ('/Users/flavio/test.txt') } catch (err) { console.error(err) } 

As informações sobre o arquivo caem na constante de stats . O que é essa informação? De fato, o objeto correspondente nos fornece um grande número de propriedades e métodos úteis:

  • Os .isFile() e .isDirectory() permitem, respectivamente, descobrir se o arquivo investigado é um arquivo ou diretório comum.
  • O método .isSymbolicLink() permite que você saiba se um arquivo é um link simbólico.
  • O tamanho do arquivo pode ser encontrado usando a propriedade .size .

Existem outros métodos aqui, mas estes são os mais usados. Veja como usá-los:

 const fs = require('fs') fs.stat('/Users/flavio/test.txt', (err, stats) => { if (err) {   console.error(err)   return } stats.isFile() //true stats.isDirectory() //false stats.isSymbolicLink() //false stats.size //1024000 //= 1MB }) 

Caminhos de arquivo no Node.js e o módulo de caminho


O caminho do arquivo é o endereço do local no sistema de arquivos em que está localizado.

No Linux e macOS, o caminho pode ser assim:

 /users/flavio/file.txt 

No Windows, os caminhos parecem um pouco diferentes:

 C:\users\flavio\file.txt 

As diferenças nos formatos de gravação de caminho ao usar diferentes sistemas operacionais devem ser observadas, dado o sistema operacional usado para implantar o servidor Node.js.

O Node.js possui um módulo de path padrão projetado para trabalhar com caminhos de arquivo. Antes de usar este módulo em um programa, ele deve estar conectado:

 const path = require('path') 

Obter informações do caminho do arquivo


Se você tiver um caminho para o arquivo, usando os recursos do módulo de path , poderá, de uma forma conveniente para percepção e processamento adicional, descobrir detalhes sobre esse caminho. É assim:

 const notes = '/users/flavio/notes.txt' path.dirname(notes) // /users/flavio path.basename(notes) // notes.txt path.extname(notes) // .txt 

Aqui, na linha de notes , o caminho do arquivo é armazenado. Os seguintes métodos do módulo de path foram usados ​​para analisar o path :

  • dirname() - retorna o diretório pai do arquivo.
  • basename() - retorna o nome do arquivo.
  • extname() - retorna a extensão do arquivo.

Você pode descobrir o nome do arquivo sem a extensão chamando o método .basename() e passando o segundo argumento que representa a extensão:

 path.basename(notes, path.extname(notes)) //notes 

OrkTrabalhando com caminhos de arquivo


Várias partes do caminho podem ser combinadas usando o método path.join() :

 const name = 'flavio' path.join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt' 

Você pode encontrar o caminho absoluto para o arquivo com base no caminho relativo, usando o método path.resolve() :

 path.resolve('flavio.txt') //'/Users/flavio/flavio.txt'       

Nesse caso, o Node.js simplesmente adiciona /flavio.txt ao caminho que leva ao diretório de trabalho atual. Se, ao chamar esse método, você passar outro parâmetro representando o caminho para a pasta, o método o utilizará como base para determinar o caminho absoluto:

 path.resolve('tmp', 'flavio.txt') // '/Users/flavio/tmp/flavio.txt'       

Se o caminho passado como o primeiro parâmetro começar com uma barra, isso significa que é um caminho absoluto.

 path.resolve('/etc', 'flavio.txt') // '/etc/flavio.txt' 

Aqui está outro método útil - path.normalize() . Ele permite que você encontre o caminho real para o arquivo usando o caminho que contém os qualificadores de caminho relativo, como um ponto ( . ), Dois períodos ( .. ) ou duas barras:

 path.normalize('/users/flavio/..//test.txt') // /users/test.txt 

Os métodos resolve() e normalize() não verificam a existência de um diretório. Eles simplesmente encontram o caminho com base nos dados transmitidos a eles.

Lendo arquivos no Node.js


A maneira mais fácil de ler arquivos no Node.js é usar o método fs.readFile() , passando o caminho para o arquivo e o retorno de chamada, que será chamado com a transferência dos dados do arquivo (ou do objeto de erro) para ele:

 fs.readFile('/Users/flavio/test.txt', (err, data) => { if (err) {   console.error(err)   return } console.log(data) }) 

Se necessário, você pode usar a versão síncrona desse método - fs.readFileSync() :

 const fs = require('fs') try { const data = fs.readFileSync('/Users/flavio/test.txt') console.log(data) } catch (err) { console.error(err) } 

Por padrão, a codificação utf8 é usada ao ler arquivos, mas a codificação também pode ser configurada independentemente, passando o parâmetro apropriado ao método.

Os fs.readFile() e fs.readFileSync() leem todo o conteúdo do arquivo na memória. Isso significa que trabalhar com arquivos grandes usando esses métodos afetará seriamente o consumo de memória do seu aplicativo e o desempenho dele. Se você precisar trabalhar com esses arquivos, é melhor usar fluxos.

Gravando arquivos no Node.js


No Node.js, é mais fácil gravar arquivos usando o método fs.writeFile() :

 const fs = require('fs') const content = 'Some content!' fs.writeFile('/Users/flavio/test.txt', content, (err) => { if (err) {   console.error(err)   return } //   }) 

Há também uma versão síncrona do mesmo método - fs.writeFileSync() :

 const fs = require('fs') const content = 'Some content!' try { const data = fs.writeFileSync('/Users/flavio/test.txt', content) //   } catch (err) { console.error(err) } 

Esses métodos, por padrão, substituem o conteúdo dos arquivos existentes. Você pode alterar o comportamento padrão usando o sinalizador apropriado:

 fs.writeFile('/Users/flavio/test.txt', content, { flag: 'a+' }, (err) => {}) 

Os sinalizadores podem ser usados ​​aqui, que já listamos na seção descritores. Detalhes sobre bandeiras podem ser encontrados aqui .

Anexar dados a um arquivo


O método fs.appendFile() (e sua versão síncrona, fs.appendFileSync() ) é convenientemente usado para anexar dados ao final do arquivo:

 const content = 'Some content!' fs.appendFile('file.log', content, (err) => { if (err) {   console.error(err)   return } //! }) 

Sobre o uso de threads


Acima, descrevemos métodos que, ao gravar em um arquivo, gravam toda a quantidade de dados transferidos para ele, após o qual, se suas versões síncronas são usadas, eles retornam o controle ao programa e, se forem usadas versões assíncronas, chamam retornos de chamada. Se esse estado de coisas não se adequar a você, seria melhor usar fluxos.

Trabalhando com diretórios no Node.js


O módulo fs fornece ao desenvolvedor muitos métodos convenientes que podem ser usados ​​para trabalhar com diretórios.

▍Verifique a existência da pasta


Para verificar se o diretório existe e se o Node.js pode acessá-lo, dadas as permissões, você pode usar o método fs.access() .

Crie uma nova pasta


Para criar novas pastas, você pode usar os fs.mkdir() e fs.mkdirSync() :

 const fs = require('fs') const folderName = '/Users/flavio/test' try { if (!fs.existsSync(dir)){   fs.mkdirSync(dir) } } catch (err) { console.error(err) } 

EadingLendo o conteúdo da pasta


Para ler o conteúdo de uma pasta, você pode usar os fs.readdir() e fs.readdirSync() . Este exemplo lê o conteúdo da pasta - ou seja, as informações sobre quais arquivos e subdiretórios ela contém e retorna seus caminhos relativos:

 const fs = require('fs') const path = require('path') const folderPath = '/Users/flavio' fs.readdirSync(folderPath) 

É assim que você pode obter o caminho completo para o arquivo:

 fs.readdirSync(folderPath).map(fileName => { return path.join(folderPath, fileName) } 

Os resultados podem ser filtrados para obter apenas os arquivos e excluir da saída do diretório:

 const isFile = fileName => { return fs.lstatSync(fileName).isFile() } fs.readdirSync(folderPath).map(fileName => { return path.join(folderPath, fileName)).filter(isFile) } 

▍ Renomear pasta


Você pode usar os fs.rename() e fs.renameSync() para renomear a pasta. O primeiro parâmetro é o caminho da pasta atual, o segundo é um novo:

 const fs = require('fs') fs.rename('/Users/flavio', '/Users/roger', (err) => { if (err) {   console.error(err)   return } // }) 

Você pode renomear a pasta usando o método síncrono fs.renameSync() :

 const fs = require('fs') try { fs.renameSync('/Users/flavio', '/Users/roger') } catch (err) { console.error(err) } 

▍ Excluir pasta


Para excluir uma pasta, você pode usar os fs.rmdir() ou fs.rmdirSync() . Deve-se notar que excluir uma pasta na qual existe algo é uma tarefa um pouco mais complicada do que excluir uma pasta vazia. Se você precisar excluir essas pastas, use o pacote fs-extra , que é muito popular e bem suportado. É um substituto para o módulo fs , expandindo seus recursos.

O método remove() do pacote fs-extra pode excluir pastas que já possuem algo.

Você pode instalar este módulo da seguinte maneira:

 npm install fs-extra 

Aqui está um exemplo de seu uso:

 const fs = require('fs-extra') const folder = '/Users/flavio' fs.remove(folder, err => { console.error(err) }) 

Seus métodos podem ser utilizados na forma de promessas:

 fs.remove(folder).then(() => { // }).catch(err => { console.error(err) }) 

A construção assíncrona / espera também é aceitável:

 async function removeFolder(folder) { try {   await fs.remove(folder)   // } catch (err) {   console.error(err) } } const folder = '/Users/flavio' removeFolder(folder) 

Módulo FS


Acima, já encontramos alguns métodos do módulo fs usados ​​ao trabalhar com o sistema de arquivos. De fato, contém muitas outras coisas úteis. Lembre-se de que ele não precisa ser instalado; para usá-lo no programa, basta conectá-lo:

 const fs = require('fs') 

Depois disso, você terá acesso aos métodos dele, dentre os quais destacamos o seguinte, alguns dos quais você já conhece:

  • fs.access() : verifica a existência de um arquivo e a capacidade de acessá-lo com base em permissões.
  • fs.appendFile() : fs.appendFile() dados a um arquivo. Se o arquivo não existir, ele será criado.
  • fs.chmod() : altera as permissões para um determinado arquivo. Métodos semelhantes: fs.lchmod() , fs.fchmod() .
  • fs.chown() : altera o proprietário e o grupo para o arquivo especificado. Métodos semelhantes: fs.fchown() , fs.lchown() .
  • fs.close() : fecha o descritor de arquivo.
  • fs.copyFile() : copia o arquivo.
  • fs.createReadStream() : cria um fluxo para ler um arquivo.
  • fs.createWriteStream() : cria um fluxo de gravação de arquivo.
  • fs.link() : cria um novo link fs.link() para o arquivo.
  • fs.mkdir() : cria um novo diretório.
  • fs.mkdtemp() : cria um diretório temporário.
  • fs.open() : abre um arquivo.
  • fs.readdir() : lê o conteúdo de um diretório.
  • fs.readFile() : lê o conteúdo de um arquivo. Método semelhante: fs.read() .
  • fs.readlink() : lê o valor de um link simbólico.
  • fs.realpath() : resolve o caminho relativo do arquivo construído usando caracteres . e .. , no caminho completo.
  • fs.rename() : renomeia um arquivo ou pasta.
  • fs.rmdir() : remove a pasta.
  • fs.stat() : retorna informações do arquivo. Métodos semelhantes: fs.fstat() , fs.lstat() .
  • fs.symlink() : cria um novo link simbólico para o arquivo.
  • fs.truncate() : trunca o arquivo no tamanho especificado. Método semelhante: fs.ftruncate() .
  • fs.unlink() : remove um arquivo ou link simbólico.
  • fs.unwatchFile() : desativa o monitoramento de alterações no arquivo.
  • fs.utimes() : altera o registro de data e hora de um arquivo. Método semelhante: fs.futimes() .
  • fs.watchFile() : habilita o monitoramento de alterações no arquivo. Método semelhante: fs.watch() .
  • fs.writeFile() : grava dados em um arquivo. Método semelhante: fs.write() .

Um recurso interessante do módulo fs é o fato de todos os seus métodos, por padrão, serem assíncronos, mas também existem versões síncronas deles, cujos nomes são obtidos adicionando a palavra Sync aos nomes dos métodos assíncronos.

Por exemplo:

  • fs.rename()
  • fs.renameSync()
  • fs.write()
  • fs.writeSync()

O uso de métodos síncronos afeta seriamente o funcionamento do programa.

O Node.js 10 fornece suporte experimental para essas APIs baseadas em promessas.

Explore o método fs.rename() . Aqui está uma versão assíncrona desse método usando retornos de chamada:

 const fs = require('fs') fs.rename('before.json', 'after.json', (err) => { if (err) {   return console.error(err) } // }) 

Ao usar sua versão síncrona, a construção try/catch é usada para manipular erros:

 const fs = require('fs') try { fs.renameSync('before.json', 'after.json') // } catch (err) { console.error(err) } 

A principal diferença entre essas opções para usar esse método é que, no segundo caso, o script será bloqueado até que a operação do arquivo seja concluída.

Módulo de caminho


O módulo path, sobre o qual também falamos sobre alguns de seus recursos, contém muitas ferramentas úteis que permitem interagir com o sistema de arquivos. Como já mencionado, você não precisa instalá-lo, pois faz parte do Node.js. Para usá-lo, basta conectá-lo:

 const path = require('path') 

A propriedade path.sep deste módulo fornece o caractere usado para separar segmentos de caminho ( \ no Windows e / no Linux e macOS), e a propriedade path.delimiter fornece o caractere usado para separar vários caminhos ( ; no Windows e : no Linux e macOS).

Vamos considerar e ilustrar alguns métodos do módulo de path .

▍path.basename ()


Retorna o último fragmento do caminho. Ao passar o segundo parâmetro para esse método, você pode remover a extensão do arquivo.

 require('path').basename('/test/something') //something require('path').basename('/test/something.txt') //something.txt require('path').basename('/test/something.txt', '.txt') //something 

▍path.dirname ()


Retorna a parte do caminho que representa o nome do diretório:

 require('path').dirname('/test/something') // /test require('path').dirname('/test/something/file.txt') // /test/something 

▍path.extname ()


Retorna a parte do caminho que representa a extensão do arquivo:

 require('path').extname('/test/something') // '' require('path').extname('/test/something/file.txt') // '.txt' 

▍path.isAbsolute ()


Retorna true se o caminho for absoluto:

 require('path').isAbsolute('/test/something') // true require('path').isAbsolute('./test/something') // false 

▍path.join ()


Conecta várias partes do caminho:

 const name = 'flavio' require('path').join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt' 

▍path.normalize ()


Tentando descobrir o caminho real com base no caminho que contém os caracteres usados ​​para construir caminhos relativos, como . , .. e // :

 require('path').normalize('/users/flavio/..//test.txt') ///users/test.txt 

▍path.parse ()


Converte um caminho em um objeto cujas propriedades representam partes individuais do caminho:

  • root : o diretório raiz.
  • dir : caminho do arquivo começando no diretório raiz
  • base : nome e extensão do arquivo.
  • name : name arquivo.
  • ext : extensão de arquivo.

Aqui está um exemplo usando este método:

 require('path').parse('/users/test.txt') 

Como resultado de seu trabalho, o seguinte objeto é obtido:

 { root: '/', dir: '/users', base: 'test.txt', ext: '.txt', name: 'test' } 

▍path.relative ()


Toma como argumento duas maneiras. Retorna o caminho relativo do primeiro caminho para o segundo, com base no diretório de trabalho atual:

 require('path').relative('/Users/flavio', '/Users/flavio/test.txt') //'test.txt' require('path').relative('/Users/flavio', '/Users/flavio/something/test.txt') //'something/test.txt' 

▍path.resolve ()


Localiza o caminho absoluto com base no caminho relativo passado para ele:

 path.resolve('flavio.txt') //'/Users/flavio/flavio.txt'      . 

Sumário


Hoje, vimos os módulos fs e path do Node.js usados ​​para trabalhar com o sistema de arquivos. Na próxima parte desta série, na qual termina, discutiremos os , events , módulos http , falar sobre como trabalhar com fluxos e com sistemas de gerenciamento de banco de dados no Node.js.

Caros leitores! Quais pacotes npm você usa ao trabalhar com o sistema de arquivos no Node.js.

Source: https://habr.com/ru/post/pt424969/


All Articles