Node.js: Sistema de Arquivos
Última atualização: 2026-08-26
Charlie é um engenheiro de back-end responsável pela manutenção da plataforma de análise de logs da empresa. Todos os dias, ao amanhecer, o sistema precisa processar aproximadamente 50.000 linhas de arquivos de log do servidor. Inicialmente, ele usava o fs.readFileSync para ler os logs um por um, mas o programa inteiro travava completamente durante o processo de leitura, fazendo com que todas as outras solicitações atingissem o tempo limite. Depois de mudar para a leitura assíncrona com fs.readFile, o programa passou a ser capaz de responder a outras solicitações enquanto aguardava a E/S do disco, resultando em um aumento de 10 vezes na taxa de transferência geral. Essa experiência proporcionou a ele uma compreensão profunda da diferença entre operações síncronas e assíncronas na API do sistema de arquivos do Node.js.
1. O que você vai aprender
- Use
fs.readFile/fs.writeFile/fs.appendFile/fs.unlinkpara realizar operações com arquivos - Use
fs.mkdir/fs.readdir/fs.stat/fs.existsSyncpara realizar operações em diretórios - Distinguir as diferenças na execução entre métodos síncronos e assíncronos
- Compreendendo o modelo “Error-First Callback”
(err, data) - Use a API
fs.promisespara realizar operações com arquivos utilizando Promises - Selecione a codificação adequada para o arquivo (utf8 / base64 / binária)
2. Visão geral do módulo fs
O módulo fs é um módulo integrado do Node.js para operações no sistema de arquivos, oferecendo recursos como leitura e gravação de arquivos, gerenciamento de diretórios e verificações de permissões. Cada operação geralmente oferece três estilos: síncrona, assíncrona com callback e assíncrona com Promise.
| Recurso | Método síncrono | Método de callback assíncrono | Método fs.promises |
|---|---|---|---|
| Característica de nomenclatura | xxxSync Sufixo |
Sem sufixo | fs.promises.xxx |
| Valor de retorno | Retorna o resultado diretamente | undefined, obtido por meio de um callback |
Retorna Promise |
| Bloqueia o ciclo de eventos | Sim | Não | Não |
| Tratamento de erros | try/catch |
Primeiro parâmetro da função de retorno de chamada | .catch() / try-catch |
| Casos de uso recomendados | Carregar a configuração na inicialização | Compatibilidade com versões anteriores | A melhor opção para novos projetos |
const fs = require('fs');
// Synchronize
const data = fs.readFileSync('config.json', 'utf8');
// Asynchronous Callbacks
fs.readFile('config.json', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// Promise
const fsPromises = require('fs/promises');
fsPromises.readFile('config.json', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
3. Sequências de execução síncronas versus assíncronas
Os métodos síncronos bloqueiam o ciclo de eventos e não continuam a executar o código subsequente até que a operação com o arquivo esteja concluída. Os métodos assíncronos, por outro lado, retornam imediatamente e notificam o resultado por meio de um callback ou de uma Promise assim que a operação com o arquivo estiver concluída.
sequenceDiagram
participant Main as Main Thread
participant FS_Sync as Synchronous Read
participant FS_Async as Asynchronous Reading
participant Disk as Disk I/O
Note over Main,Disk: Synchronous Execution Process
Main->>FS_Sync: readFileSync('log.txt')
FS_Sync->>Disk: Read a File(Blocking Wait)
Disk-->>FS_Sync: Return Data
FS_Sync-->>Main: Continue executing the following code
Note right of Main: All other requests are on hold
Note over Main,Disk: Asynchronous Execution Flow
Main->>FS_Async: readFile('log.txt', callback)
FS_Async->>Disk: Submit a read request
FS_Async-->>Main: Return Now,Continue Execution
Note right of Main: Can handle other requests
Disk-->>FS_Async: I/O Done
FS_Async-->>Main: Execute the callback function
▶ Exemplo: A leitura síncrona bloqueia todo o programa
const fs = require('fs');
console.log('Start Reading...');
const data = fs.readFileSync('big-log.txt', 'utf8');
console.log('Reading complete,Number of lines:', data.split('\n').length);
console.log('This line must wait until the data has finished loading before it is executed.');
Start Reading...
Reading complete,Number of lines:50000
This line must wait until the data has finished loading before it is executed.
▶ Exemplo: Leitura assíncrona sem bloquear o ciclo de eventos
const fs = require('fs');
console.log('Start Reading...');
fs.readFile('big-log.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('Reading complete,Number of lines:', data.split('\n').length);
});
console.log('This line is executed immediately,No need to wait for the data to be read');
Start Reading...
This line is executed immediately,No need to wait for the data to be read
Reading complete,Number of lines:50000
4. Operações de leitura e gravação em arquivos
| Método | Parâmetros | Finalidade |
|---|---|---|
fs.readFile(path, encoding, callback) |
Caminho, codificação, callback | Leitura assíncrona de todo o arquivo |
fs.readFileSync(path, encoding) |
Caminho, codificação | Ler o arquivo inteiro de forma síncrona |
fs.writeFile(path, data, encoding, callback) |
Caminho, Dados, Codificação, Callback | Gravação assíncrona (sobrescrita) |
fs.writeFileSync(path, data, encoding) |
Caminho, Dados, Codificação | Gravação em sincronia (sobrescrita) |
fs.appendFile(path, data, encoding, callback) |
Caminho, Dados, Codificação, Callback | Acrescentamento assíncrono |
fs.appendFileSync(path, data, encoding) |
Caminho, Dados, Codificação | Sincronizar conteúdo anexado |
fs.unlink(path, callback) |
Caminho, Callback | Exclusão assíncrona de arquivos |
fs.unlinkSync(path) |
Caminho | Excluir arquivos simultaneamente |
▶ Exemplo: Gravação e adição de dados a arquivos
const fs = require('fs');
fs.writeFile('output.txt', 'Content on the first line\n', 'utf8', (err) => {
if (err) throw err;
console.log('Write complete');
fs.appendFile('output.txt', 'The second line added\n', 'utf8', (err) => {
if (err) throw err;
console.log('Addition Complete');
fs.readFile('output.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('Document Content:\n', data);
});
});
});
Write complete
Addition Complete
Document Content:
Content on the first line
The second line added
▶ Exemplo: Usando fs.promises para evitar callbacks aninhados
const fs = require('fs/promises');
async function writeAndRead() {
try {
await fs.writeFile('output.txt', 'Content on the first line\n', 'utf8');
console.log('Write complete');
await fs.appendFile('output.txt', 'The second line added\n', 'utf8');
console.log('Addition Complete');
const data = await fs.readFile('output.txt', 'utf8');
console.log('Document Content:\n', data);
} catch (err) {
console.error('Operation Failed:', err.message);
}
}
writeAndRead();
▶ Exemplo: Exclusão de um arquivo
const fs = require('fs/promises');
async function deleteFile() {
try {
await fs.unlink('output.txt');
console.log('The file has been deleted');
} catch (err) {
console.error('Deletion Failed:', err.message);
}
}
deleteFile();
5. Operações com diretórios
| Método | Parâmetros | Finalidade |
|---|---|---|
fs.mkdir(path, options, callback) |
Caminho, {recursive}, Callback |
Criar diretório |
fs.readdir(path, options, callback) |
Caminho, {withFileTypes}, Callback |
Listar o conteúdo do diretório |
fs.stat(path, callback) |
Caminho, Callback | Obter informações sobre arquivos/diretórios |
fs.existsSync(path) |
Caminho | Verificar se o caminho existe |
▶ Exemplo: Criando um diretório e listando seu conteúdo
const fs = require('fs/promises');
async function dirOperations() {
try {
await fs.mkdir('logs', { recursive: true });
console.log('The directory was successfully created');
await fs.writeFile('logs/app.log', '2025-01-01 Server started\n', 'utf8');
await fs.writeFile('logs/error.log', '2025-01-01 Connection timeout\n', 'utf8');
const files = await fs.readdir('logs');
console.log('Table of Contents:', files);
} catch (err) {
console.error('Operation Failed:', err.message);
}
}
dirOperations();
The directory was successfully created
Table of Contents: [ 'app.log', 'error.log' ]
▶ Exemplo: Como determinar se é um arquivo ou um diretório
const fs = require('fs/promises');
async function checkType() {
const stats = await fs.stat('logs');
console.log('logs This is the table of contents:', stats.isDirectory());
console.log('logs It is a file:', stats.isFile());
const fileStats = await fs.stat('logs/app.log');
console.log('app.log It is a file:', fileStats.isFile());
console.log('File size:', fileStats.size, 'bytes');
}
checkType();
logs This is the table of contents: true
logs It is a file: false
app.log It is a file: true
File size: 29 bytes
6. Comparação entre callbacks do tipo “error-first” e promessas
A API do sistema de arquivos do Node.js segue a convenção de “callback com erro em primeiro lugar”: o primeiro argumento da função de callback é sempre um objeto de erro; se não houver erro, é null. fs.promises utiliza o mecanismo padrão de Promise para lidar com erros.
| Item de comparação | Callback “Error-First” | fs.promises |
|---|---|---|
| Assinatura da função | (err, data) => {} |
Retorna Promise<data> |
| Erro de julgamento | if (err) Verificar |
try/catch ou .catch() |
| Problemas de aninhamento | Propenso ao “callback hell” | async/await Simplificação |
| Cenários típicos | Compatibilidade com projetos legados | Recomendações para novos projetos |
| Método de importação | require('fs') |
require('fs/promises') |
▶ Exemplo: Uma comparação entre dois métodos de tratamento de erros
const fsCallback = require('fs');
const fsPromise = require('fs/promises');
// Error-First Callback
fsCallback.readFile('not-exist.txt', 'utf8', (err, data) => {
if (err) {
console.error('Callback Method - Error:', err.code);
return;
}
console.log(data);
});
// Promise Method
async function readWithPromise() {
try {
const data = await fsPromise.readFile('not-exist.txt', 'utf8');
console.log(data);
} catch (err) {
console.error('PromiseMethod - Error:', err.code);
}
}
readWithPromise();
Callback Method - Error: ENOENT
PromiseMethod - Error: ENOENT
7. Codificação de arquivos
| Código | Descrição | Cenários aplicáveis | Exemplo |
|---|---|---|---|
'utf8' |
Codificação de texto UTF-8 (padrão) | Leitura e gravação de arquivos de texto | Logs, configurações, JSON |
'base64' |
Codificação Base64 | Transferência de imagens, conversão de binário para texto | Imagens incorporadas, anexos de e-mail |
'binary' ('latin1') |
Bytes brutos | Operações de baixo nível com arquivos binários | Processamento de imagens e arquivos compactados |
null |
Retornar ao objeto buffer | É necessário manipular bytes brutos | Verificação de arquivos, processamento de fluxos |
▶ Exemplo: Lendo o mesmo arquivo com diferentes codificações
const fs = require('fs/promises');
async function readEncodings() {
await fs.writeFile('sample.txt', 'Hello The World', 'utf8');
const utf8Data = await fs.readFile('sample.txt', 'utf8');
console.log('UTF-8:', utf8Data);
const base64Data = await fs.readFile('sample.txt', 'base64');
console.log('Base64:', base64Data);
const bufferData = await fs.readFile('sample.txt');
console.log('Buffer:', bufferData);
console.log('Buffer Hexadecimal:', bufferData.toString('hex'));
}
readEncodings();
UTF-8: Hello The World
Base64: SGVsbG8g5LiW55WM
Buffer: <Buffer 48 65 6c 6c 6f 20 e4 b8 96 e7 95 8c>
Buffer Hexadecimal: 48656c6c6f20e4b896e7958c
8. Exemplo abrangente: Ferramenta de gerenciamento de arquivos
Crie um fluxo de trabalho completo para gerenciamento de arquivos: Criar um diretório → Escrever a configuração → Ler e analisar → Adicionar ao log → Listar o conteúdo.
const fs = require('fs/promises');
const path = require('path');
async function fileManager() {
const dir = 'project-data';
const configPath = path.join(dir, 'config.json');
const logPath = path.join(dir, 'app.log');
try {
// Step 1: Create a Directory
await fs.mkdir(dir, { recursive: true });
console.log('✓ The table of contents has been created:', dir);
// Step 2: Write to the configuration file
const config = {
appName: 'LogAnalyzer',
version: '1.0.0',
maxLines: 50000,
encoding: 'utf8'
};
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf8');
console.log('✓ The configuration file has been written.:', configPath);
// Step 3: Read and Parse the Configuration
const raw = await fs.readFile(configPath, 'utf8');
const parsed = JSON.parse(raw);
console.log('✓ Configuration loaded:', parsed.appName, 'v' + parsed.version);
// Step 4: Add a log entry
const timestamp = new Date().toISOString();
await fs.appendFile(logPath, `[${timestamp}] Service started\n`, 'utf8');
await fs.appendFile(logPath, `[${timestamp}] Config loaded: ${parsed.maxLines} lines\n`, 'utf8');
console.log('✓ The log has been appended.:', logPath);
// Step 5: List the contents of the table of contents
const entries = await fs.readdir(dir, { withFileTypes: true });
console.log('✓ Table of Contents:');
for (const entry of entries) {
const type = entry.isDirectory() ? '[DIR]' : '[FILE]';
const stats = await fs.stat(path.join(dir, entry.name));
console.log(` ${type} ${entry.name} (${stats.size} bytes)`);
}
} catch (err) {
console.error('✗ Operation Failed:', err.message);
}
}
fileManager();
✓ The table of contents has been created: project-data
✓ The configuration file has been written.: project-data/config.json
✓ Configuration loaded: LogAnalyzer v1.0.0
✓ The log has been appended.: project-data/app.log
✓ Table of Contents:
[FILE] app.log (106 bytes)
[FILE] config.json (98 bytes)
❓ Perguntas Frequentes
P: Quando os métodos síncronos devem ser usados? R: Use-os apenas para operações únicas, como carregar arquivos de configuração, durante a inicialização do aplicativo. Nunca use métodos síncronos em tempo de execução, pois eles bloquearão o ciclo de eventos.
P: Por que o primeiro parâmetro da função de retorno de chamada é
err? R: Essa é a convenção de “função de retorno de chamada com erros em primeiro lugar” do Node.js, que exige que os desenvolvedores verifiquem se há erros antes de processar os dados, evitando assim que exceções sejam ignoradas.
P: Qual é a diferença entre
fs.promisesefs? R:fs.promises(ourequire('fs/promises')) oferece a mesma funcionalidade, mas retorna uma Promise;async/awaitpode ser usado como alternativa às chamadas de retorno aninhadas;fsusa o estilo de callback — ambos são funcionalmente idênticos.
P: Como posso determinar se um caminho é um arquivo ou um diretório? R: Use
fs.stat(path)para obter o objetostats; em seguida, chamestats.isFile()para verificar se é um arquivo estats.isDirectory()para verificar se é um diretório.
P: O
readFilecarrega o arquivo inteiro na memória? R: Sim, oreadFilelê o arquivo inteiro na memória. Ao trabalhar com arquivos grandes, você deve usar ofs.createReadStreampara lê-los em fluxos, a fim de evitar um estouro de memória.
P: Qual é a finalidade da opção
recursiveemfs.mkdir? R: Ao definir{ recursive: true }, é possível criar vários níveis de diretórios aninhados de uma só vez, de forma semelhante amkdir -p, e o sistema não exibirá um erro mesmo que o diretório já exista.
📖 Resumo
- Conceitos-chave e como aplicá-los
- Visão geral dos conceitos-chave e do uso do módulo fs
- Conceitos fundamentais e uso de sequências de execução síncronas versus assíncronas
- Conceitos básicos e uso das operações de leitura e gravação de arquivos
- Conceitos básicos e uso das operações de diretório
- Conceitos-chave e uso de callbacks do tipo “error-first” em comparação com promessas
- Conceitos básicos e uso da codificação de arquivos
- Exemplo abrangente: conceitos básicos e uso de ferramentas de gerenciamento de arquivos
📝 Exercícios
- Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
- Modifique o exemplo completo e adicione suas próprias extensões
- Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
- Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
- Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.