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 raizbase
: 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.
