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.

[Aconselhamos a ler] Outras partes do cicloParte 1: 
Informações gerais e introduçãoParte 2: 
JavaScript, V8, alguns truques de desenvolvimentoParte 3: 
Hospedagem, REPL, trabalho com o console, módulosParte 4: 
arquivos npm, package.json e package-lock.jsonParte 5: 
npm e npxParte 6: 
loop de eventos, pilha de chamadas, temporizadoresParte 7: 
Programação assíncronaParte 8: 
Guia Node.js, Parte 8: Protocolos HTTP e WebSocketParte 9: 
Guia Node.js, parte 9: trabalhando com o sistema de arquivosParte 10: 
Guia do Node.js, Parte 10: Módulos padrão, fluxos, bancos de dados, NODE_ENVPDF completo do Guia Node.js. 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')  
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)  
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')  
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')  
▍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:- namearquivo.
- 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.
