Boas-vindas ao curso de Streams com o Node.js!
Audiodescrição: Thiago Bussola é um homem branco, com cabelo curto e barba. Estou vestindo uma camiseta rosa e óculos de grau, e atrás dele há uma estante e um armário branco iluminado por luzes azuis.
Este conteúdo é destinado a quem já possui experiência na criação de APIs com Node.js, seja utilizando Express ou outro framework, e deseja aprender a trabalhar com arquivos mais pesados, como relatórios, arquivos de vídeo ou até mesmo arquivos de áudio.
Neste curso, nós aprenderemos a entender como o loop de eventos do Node.js se comporta com arquivos mais pesados, a ler, escrever e manipular arquivos com uma grande quantidade de linhas, a fazer upload e streaming de arquivos de vídeo, e a integrar nossa aplicação com uma API da OpenAI para expandir as possibilidades em nosso serviço de streaming. Além disso, abordaremos o tratamento de erros baseado nos eventos das nossas streams.
Tudo isso será explorado em um projeto prático, no qual simularemos uma plataforma de videoaulas.
É necessário saber como desenvolver APIs REST usando Express ou outro framework e ter um entendimento básico de manipulação de arquivos de texto com Node.js.
Aproveite os recursos da nossa plataforma, pois além dos vídeos, oferecemos o apoio do fórum e uma comunidade no Discord para auxiliar nos estudos.
Vamos estudar?
Na atividade de preparação do ambiente, executamos um script para criar um arquivo que simula, por exemplo, um relatório de um serviço de streaming, contendo:
O arquivo acabou ficando bem pesado, e nosso script foi criado para ter pelo menos 2 GB. Nesta aula, tentaremos fazer a leitura de um arquivo grande como esse, utilizando o Node.js da forma padrão, que é utilizando o readFile()
. Para isso, vamos criar uma pasta chamada "services" dentro da pasta "src", e um arquivo chamado file.services.ts
.
Nesse arquivo, criaremos uma constante chamada filename
, que receberá o nome do arquivo que queremos ler, largeFile.csv
. Em seguida, criaremos uma função assíncrona chamada brokenApp()
, que não receberá nenhum parâmetro. Nessa função, chamaremos o await readFile
, importando-o do fs/promises
, e passaremos o nome do arquivo armazenado na constante filename
. O segundo parâmetro, que é opcional, será o encoding do arquivo, que pode ser utf-8
, já que está em português brasileiro.
const filename = "largeFile.csv";
async function brokenApp() {
await readFile(filename, "utf8");
}
brokenApp();
A partir da linha 9, chamaremos a função brokenApp()
, salvaremos o arquivo, abriremos o terminal e executaremos o arquivo. Como estamos utilizando TypeScript, precisamos de um transpilador para lê-lo. Usaremos o npx
, que não requer instalação da dependência, e executaremos com o comando:
npx ts-node-dev src/services/file.service.ts
O Node.js lançará um erro indicando que o arquivo não pode ser maior do que 2 GB, e que não consegue manipulá-lo. Isso ocorre devido a uma trava de segurança padrão para evitar a leitura ou manipulação de arquivos desse tamanho. O Node.js funciona com um sistema chamado Event Loop, que é uma parte single-thread da aplicação, responsável por escutar eventos e delegar funções para outras partes do sistema. Embora o loop seja single-thread, outras ações, como entrada e saída, não são. Se travarmos o loop de eventos, não conseguimos delegar outras tarefas na aplicação.
Existem técnicas para trabalhar com arquivos desse tamanho, como o uso de streams. Streams são funções que permitem a leitura de arquivos em pequenos pedaços, sem carregar tudo na memória de uma vez só, sendo processado incrementalmente. Vamos ver como fica a leitura desse arquivo usando streams, ao invés de carregar o arquivo inteiro na memória, como o readFile()
faz.
Antes da linha com brokenApp()
, criaremos outra função, a readLargeFile()
, que também não recebe parâmetros, além implementarmos um createReadStream
do módulo fs
, passando o arquivo que queremos ler e um objeto com a propriedade encoding
, utilizando utf8
:
function readLargeFile() {
createReadStream(filename, { encoding: "utf8" });
}
//brokenApp();
Vamos executar a leitura do arquivo sem armazená-lo em uma constante, apenas para verificar a velocidade com que o Node.js consegue ler um arquivo desse tamanho. Comentaremos a função brokenApp()
e chamaremos readLargeFile()
. Após salvar, executaremos novamente o arquivo file.service.ts
.
A leitura foi tão rápida que o terminal nem conseguiu calcular o tempo, ocorrendo em menos de um segundo. Agora, incluiremos uma constante para imprimir todo o processo de leitura e verificar se realmente está acontecendo.
Criaremos uma constante chamada readStream
, que receberá createReadStream()
. Utilizaremos readStream.on()
, que permite escutar eventos dentro da stream. Queremos ouvir o evento de data
, ou seja, os dados que chegam na stream. Passaremos uma função anônima com um parâmetro chunk
, utilizando uma arrow function, e dentro dela colocaremos console.log(chunk)
. O chunk
é o pequeno pedaço que estamos tentando ler.
function readLargeFile() {
const readStream = createReadStream(filename, { encoding: "utf8" });
readStream.on("data", (chunk) => {
console.log(chunk);
});
}
Durante o processo de leitura, ele parará e imprimirá cada chunk
. Como aumentamos o processo de entrada e saída, é normal que demore um pouco mais, mas conseguiremos verificar se todo o arquivo está sendo lido corretamente. Executaremos novamente o comando no terminal.
Vimos que está lendo, e não passaremos pela leitura do arquivo inteiro. Nosso CSV está sendo lido corretamente, passando o nome da pessoa, a série que ela assistiu e a nota atribuída, assim como o nosso CSV original.
A seguir, aprenderemos como manipular os dados do nosso relatório, para irmos além da leitura. Até a próxima!
Anteriormente, aprendemos a fazer a leitura de um grande arquivo usando as Node.js streams. No entanto, apenas ler um arquivo ou um relatório não é o suficiente. No nosso dia a dia de trabalho, muitas vezes queremos processar esse tipo de relatório, seja adicionando novos campos ou transformando alguns dados pré-existentes. Vamos transformar os dados, modificando o que já existe e adicionando uma nova coluna no nosso CSV.
No caso dos nomes, podemos usar o comando cat
para visualizar o arquivo largeFile.csv
. Isso é útil, pois abrir o arquivo no VSCode ou em um editor de CSV pode causar lentidão. Observamos que o nome e o sobrenome das pessoas começam com letras maiúsculas. Supondo que nosso banco de dados seja case sensitive, queremos normalizar os dados, convertendo todos para minúsculas ou maiúsculas.
Além disso, cada campo está separado por vírgula, e desejamos incluir um campo adicional que registre a data em que fizemos a alteração no nome ou qualquer outra modificação no relatório.
Vamos minimizar o terminal e definir a lógica que utilizaremos. Começaremos transformando cada vírgula em um índice de um array. Vamos pegar o primeiro índice, zero, converter todo o valor para maiúsculas, criar um novo campo para a data e, em seguida, juntar novamente os dados usando vírgulas para recriar a "string" do CSV.
Para criar a lógica e esse processo de transformação, abaixo da implementação do readLargeFile()
e antes da linha com //brokenApp();
, vamos criar uma função chamada transformCsvLine()
, que receberá um parâmetro do tipo string, representando a linha do CSV. Dentro dessa função, criaremos uma constante chamada parts
, que receberá a linha do CSV dividida em um array, usando o método split()
com a vírgula como separador.
A lógica será processar o primeiro índice apenas se o array tiver três itens, ou seja, se o CSV tiver três colunas. Caso contrário, não faremos a transformação, pois faltam dados no relatório. Usaremos um if
para verificar se parts.length
é igual a 3. Se for, processaremos o dado.
Vamos pegar o parts
no índice 0, que contém o nome da pessoa, e aplicar trim()
para remover espaços em branco, seguido de toUpperCase()
para converter para maiúsculas. Caso desejemos converter para minúsculas, usamos toLowerCase()
.
Em seguida, criaremos uma constante alterationDate
, que receberá um new Date()
formatado como texto no formato ISO, usando .toISOString()
. Retornaremos um array com os índices originais e o alterationDate
como quarto índice, usando o operador de espalhamento ...
para incluir os elementos de parts
.
Por fim, usaremos join()
para juntar os elementos do array em uma string, separando-os por vírgulas, e adicionaremos \n
para quebrar a linha. Caso a linha não atenda à nossa regra, retornaremos a linha original com uma quebra de linha. O trecho de código, então, ficará da seguinte forma:
function transformCsvLine(line: string) {
const parts = line.split(",")
if(parts.length === 3) {
parts[0] = parts[0].trim().toUpperCase()
const alterationDate = new Date().toISOString()
return [...parts, alterationDate].join(",") + "\n"
}
return line + "\n";
}
Adiante, aprenderemos como aplicar o processo de transformação usando a lógica que acabamos de criar. Vejo vocês a seguir!
O curso Node.js: dominando streams e processando arquivos pesados possui 94 minutos de vídeos, em um total de 47 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.