Leia arquivos no Node.js com eficiência usando o módulo fs
Use readFile() de node:fs/promises quando precisar do conteúdo completo de um arquivo pequeno. Para um
arquivo grande, processe um stream, um chunk de cada vez. Os exemplos abaixo leem a mesma entrada
como texto, contam seus bytes e inspecionam seus dez primeiros bytes sem alterar o arquivo.
Prepare um arquivo de exemplo
Estes exemplos foram testados com o Node.js v26.8.2 no macOS. A preparação usa Bash; não é preciso
instalar nenhum pacote. Salve os exemplos em JavaScript com os nomes de arquivo .mjs indicados
para que o Node.js os carregue como módulos ES,
inclusive dentro de um projeto CommonJS.
Execute isto em um diretório onde você quer criar a demonstração:
mkdir node-read-demo &&
cd node-read-demo &&
printf 'Hello, 🌍!\n' > example.txt
A preparação se recusa a usar um diretório node-read-demo já existente. Se mkdir ou cd falhar,
a cadeia && para antes de gravar example.txt. Em caso de falha, interrompa e escolha um local
novo. Depois que der certo, permaneça em node-read-demo e salve cada script ali. Os scripts apenas
imprimem resultados no terminal; executá-los novamente deixa sua entrada inalterada.
Leia um arquivo UTF-8 pequeno
Salve isto como read-text.mjs:
import { readFile } from 'node:fs/promises'
async function main() {
const path = process.argv[2] ?? 'example.txt'
const text = await readFile(path, 'utf8')
process.stdout.write(text)
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
Execute-o a partir do diretório da demonstração:
node read-text.mjs example.txt
A saída é Hello, 🌍! seguido de uma quebra de linha, exatamente como está armazenado no arquivo. O
argumento opcional tem example.txt como padrão. Caminhos de entrada relativos são resolvidos a
partir do diretório de trabalho atual do terminal, não do diretório do script.
O argumento 'utf8' faz com que readFile()
retorne uma string. Omita-o quando precisar de um Buffer com os bytes originais, como uma
imagem ou um arquivo compactado. A decodificação UTF-8 serve para texto, não para dados binários
arbitrários.
Processe um arquivo grande sem acumulá-lo
Embora readFile() seja assíncrono, ele ainda carrega o resultado inteiro na memória. Use-o quando
isso couber no orçamento de memória da sua aplicação, incluindo leituras simultâneas. Promise.all()
aplicado a uma lista ilimitada de arquivos pode reter muitos resultados completos ao mesmo tempo.
Um stream permite consumir e descartar chunks. Salve isto como count-bytes.mjs para contar os bytes
efetivamente lidos:
import { createReadStream } from 'node:fs'
async function main() {
const path = process.argv[2] ?? 'example.txt'
let total = 0
for await (const chunk of createReadStream(path)) {
total += chunk.length
}
console.log(`Read ${total} bytes.`)
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
node count-bytes.mjs example.txt
Isto imprime Read 13 bytes. O globo ocupa quatro bytes em UTF-8, então contar caracteres daria
uma resposta diferente. Nenhuma codificação é definida no stream: cada chunk é um Buffer de
bytes brutos. Um arquivo vazio imprime Read 0 bytes.
O loop for await...of consome
o stream e propaga falhas de leitura para catch. Por padrão, o stream de arquivo fecha seu
descritor ao concluir ou em caso de erro. Este exemplo mantém um contador em vez de todos os chunks;
acrescentar cada chunk a um array ou a uma string eliminaria esse benefício de memória.
Para adaptar o loop a um processamento sequencial, faça o trabalho dentro dele e aguarde com
await o trabalho assíncrono antes de continuar. Um chunk não é necessariamente uma linha
completa, um objeto JSON ou um registro CSV. Use um parser que trate essas fronteiras se sua tarefa
precisar desses registros. Se você só precisa do tamanho informado de um arquivo, stat()
evita totalmente a leitura do conteúdo.
Leia apenas os dez primeiros bytes
Para inspecionar um cabeçalho binário, abra um FileHandle e leia um prefixo limitado. Salve isto
como read-prefix.mjs:
import { open } from 'node:fs/promises'
async function main() {
const path = process.argv[2] ?? 'example.txt'
const handle = await open(path, 'r')
try {
const buffer = Buffer.alloc(10)
let total = 0
while (total < buffer.length) {
const { bytesRead } = await handle.read(buffer, total, buffer.length - total, total)
if (bytesRead === 0) break
total += bytesRead
}
console.log(`Read ${total} bytes: ${buffer.subarray(0, total).toString('hex')}`)
} finally {
await handle.close()
}
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
node read-prefix.mjs example.txt
Isto imprime Read 10 bytes: 48656c6c6f2c20f09f8c. O último argumento de
handle.read() é a
posição no arquivo; aqui ela avança a partir de zero. Uma leitura pode retornar menos bytes do que o
solicitado, então o loop continua até preencher dez bytes ou chegar ao EOF (bytesRead === 0).
finally fecha o handle mesmo se a leitura falhar.
O subarray() limita a saída aos
bytes efetivamente lidos, excluindo o espaço não utilizado no caso de um arquivo curto ou vazio. A
saída em hexadecimal também evita decodificar um caractere UTF-8 incompleto: este prefixo de dez
bytes termina no meio do globo.
Diagnostique uma leitura com falha
Tente um nome de arquivo que não existe:
node read-text.mjs missing.txt
Isto imprime Read failed: ENOENT na saída de erro padrão e encerra com o status de saída 1. Os três
scripts relatam falhas de leitura dessa forma. Definir
process.exitCode sinaliza a falha e
ainda permite que a saída pendente seja concluída.
Para ENOENT, verifique o caminho de entrada e o diretório de trabalho. EACCES indica um
problema de permissões; verifique se o processo consegue percorrer os diretórios pai e ler o
arquivo. Tente a leitura diretamente em vez de verificar antes com access(): o Node.js documenta a
condição de corrida entre verificar e abrir.
Adapte a API ao código existente
A forma com callback de fs.readFile()
continua sendo suportada. Em código baseado em callbacks, inspecione o primeiro argumento error
antes de usar os dados; você não precisa converter o código ao redor para promises só por causa de
uma leitura.
fs.readFileSync() bloqueia a execução do
JavaScript até terminar. Ele pode ser adequado para um script curto de linha de comando ou para
carregar configurações na inicialização. Mantenha leituras bloqueantes fora de handlers de
requisição que precisam atender outros clientes enquanto a E/S de disco está pendente.
