‫-: Tudo bem, agora vamos trabalhar com streams.
‫E novamente, começando pela criação de um novo arquivo.
‫Tudo bem. Agora, digamos que por
‫algum motivo em nosso aplicativo, precisamos ler um grande arquivo de texto
‫do sistema de arquivos e, em seguida, enviá-lo ao cliente.
‫Então, como faríamos isso?
‫Bem, existem várias maneiras e vamos explorar algumas
‫delas, começando com a mais básica
‫e avançando até a melhor maneira de fazer isso.
‫Ok, então a primeira coisa é exigir o pacote
‫do sistema de arquivos como antes, e também o módulo HTTP.
‫Deixe-me mostrar um bom truque, então vamos
‫criar um servidor como este.
‫Portanto, exija o pacote HTTP e, a partir daqui,
‫podemos chamar createServer imediatamente. Então, assim.
‫Ok, então o resultado de requerer HTTP, é o
‫objeto HTTP e nele podemos então usar createServer.
‫Assim, e depois salve em uma variável.
‫Portanto, essa é mais uma maneira de criar um novo
‫servidor escrevendo um pouco menos de código.
‫Ok e assim como antes, vamos agora listar o evento
‫de solicitação e especificar nosso retorno de chamada aqui.
‫Portanto, a primeira solução que vamos usar
‫é a mais fácil e direta.
‫Que é simplesmente ler o arquivo em uma variável
‫e, uma vez feito isso, enviá-lo
‫ao cliente da maneira que já sabemos fazer.
‫Então esse é muito simples, e deixe-me escrever
‫um comentário aqui para isso. Portanto, solução um
‫e assim por diante. leiaFile e então nosso arquivo de teste novamente.
‫E então, quando estiver pronto, chamamos nossa função de retorno de
‫chamada na qual temos acesso aos dados.
‫Mas primeiro vamos lidar com o erro.
‫Assim, por exemplo, no caso de o arquivo não existir,
‫nesse caso, simplesmente registramos o erro no console.
‫Caso contrário, simplesmente enviamos esses dados de volta ao cliente.
‫Portanto, usamos o objeto de resposta exatamente como
‫fizemos antes, muitas vezes, e então. fim e os dados.
‫Então, salve, e essa é a primeira e
‫mais simples solução, certo?
‫Mas antes de podermos testar isso, precisamos
‫iniciar um servidor, certo? Então, usamos a escuta para fazer isso.
‫E a porta, a mesma de antes, então localhost e nossa função
‫de retorno de chamada.
‫Ok, então vamos ver se isso realmente funciona.
‫E claro que não, porque eu nem mesmo iniciei
‫o aplicativo.
‫Agora diz ouvir e agora vamos ver o
‫que acontece aqui.
‫E aqui vamos nós. Então esse arquivo é
‫enorme, tem Node. js é o melhor escrito
‫nele como 10.000 vezes ou algo assim, e por isso leva
‫muito tempo até que carregue completamente. E não estamos realmente interessados
‫em carregar tudo, então vamos parar com isso e
‫voltar ao nosso código. Então isso funciona muito bem,
‫a solução que temos aqui neste caso, mas o problema
‫é que com essa solução, o nó
‫realmente terá que carregar o arquivo inteiro na
‫memória, porque somente depois que estiver pronto, ele pode
‫enviar esses dados . Agora, isso é um problema quando
‫o arquivo é grande e também quando há uma tonelada de solicitações
‫chegando ao seu servidor. Como o processo
‫de nó ficará sem recursos muito rapidamente e seu aplicativo irá
‫parar de funcionar, tudo irá travar e seus usuários não ficarão
‫felizes, acredite em mim. Portanto, esta solução
‫aqui funciona quando estamos apenas criando algo
‫pequeno localmente apenas para nós, por exemplo.
‫Mas em um aplicativo pronto para produção, você não pode usar um
‫trecho de código como este. OK? Então, vamos prosseguir para
‫nossa segunda solução. E nessa
‫solução, vamos realmente usar streams.
‫Ok, então vamos comentar esta parte e
‫passar para a solução número dois.
‫E a ideia aqui é que
‫realmente não precisamos ler esses dados do arquivo em uma variável, certo?
‫Não precisamos dessa variável. Portanto, em vez de
‫ler os dados em uma variável e ter que armazenar
‫essa variável na memória, vamos apenas criar um fluxo legível.
‫Então, conforme recebemos cada bloco de dados, nós
‫os enviamos ao cliente como uma
‫resposta que é um fluxo gravável, lembre-se.
‫Deixe-me mostrar como podemos usar streams.
‫Portanto, criamos uma variável, vamos chamá-la de legível aqui e, a
‫partir do sistema de arquivos novamente, usamos createReadStream.
‫Então, isso é claro, pequeno. E agora o nome do arquivo
‫que estamos tentando ler. Então, isso é novamente, arquivo de teste. TXT.
‫Ok, perfeito. Isso agora cria um fluxo dos
‫dados que estão neste arquivo de texto, que podemos
‫consumir peça por peça. Então, pedaço por pedaço.
‫E como nós fazemos isso? Bem, lembre-se da
‫última aula, que cada vez que há um
‫novo dado que podemos consumir, um fluxo legível emite
‫o evento de dados. Ok, então podemos ouvir
‫isso, assim como aprendemos na palestra de eventos.
‫Tão legível. on, data e, em seguida, nossa função de retorno de chamada.
‫E em nossa função de retorno de chamada, temos acesso a
‫esses dados, portanto, a esse pedaço.
‫Deixe-me chamá-lo de chunk aqui em nossa
‫função de retorno de chamada e agora podemos lidar com esse dado.
‫E o que vamos fazer
‫com esse pedaço de dados, com esse pedaço, é
‫realmente gravá-lo em um fluxo gravável, que é a resposta.
‫Então, res. escrever, este pedaço. OK?
‫Portanto, lembre-se novamente de que essa resposta
‫é um fluxo gravável. Assim como mencionei
‫no vídeo anterior, certo? E agora podemos usar
‫o método write para enviar cada pedaço de dados para esse fluxo.
‫Ok, e com isso efetivamente estamos
‫transmitindo o conteúdo do arquivo direto para o cliente.
‫Ok, você entende a diferença?
‫Portanto, antes, escrevemos tudo de uma vez em uma variável e,
‫quando ela estava pronta, enviamos todos os dados de
‫volta para o cliente. Mas nessa
‫situação, com o riacho, é diferente.
‫Estamos efetivamente fazendo streaming do arquivo, então lemos
‫uma parte do arquivo e, assim que
‫estiver disponível, enviamos direto para o cliente, usando o método write do
‫fluxo de resposta. Então, quando a próxima
‫parte estiver disponível, essa parte será enviada, e todo o
‫caminho até que todo o arquivo seja lido e transmitido ao cliente.
‫Ok, agora só para terminar, também temos que lidar com
‫o evento quando todos os dados forem lidos.
‫Então, quando o fluxo basicamente terminar de ler os dados
‫do arquivo.
‫Nesse caso, o evento final será emitido
‫e, assim que isso acontecer, o que faremos é
‫fazer res. fim ok? E nós usamos
‫este antes, então encerrando a resposta, fizemos isso antes, certo?
‫E agora, na verdade, faz mais sentido, porque, novamente,
‫a resposta também é um fluxo e o método
‫final sinaliza que nenhum outro dado será gravado
‫neste fluxo gravável, certo?
‫Então aqui tudo o que fizemos foi usar res. terminar com os dados
‫nele. Então não fizemos streaming,
‫tudo o que fizemos foi enviar alguns dados.
‫Agora, neste caso, não estamos passando
‫nada para este método end, porque já enviamos todos os
‫dados usando res. escrever, pedaço por
‫pedaço, e então quando o fluxo legível terminar
‫de ler seu arquivo, tudo o que temos que fazer é
‫sinalizar que estamos prontos usando res. acabar assim, ok?
‫Portanto, sempre precisamos usar esses dados e esse evento final aqui, um
‫após o outro, assim. Caso contrário,
‫a resposta nunca será realmente
‫enviada ao cliente. Ok, então sem esta
‫peça aqui, toda a solução não funcionaria, ok?
‫Então, vamos fechar este aqui, começar de novo
‫e ler novamente, e você verá que está realmente
‫funcionando de novo, ok? Agora desta vez sem os problemas que
‫tivemos com a primeira solução. Vamos parar com isso aqui
‫e voltar ao nosso código, porque quero mostrar
‫a vocês agora que há outro evento que podemos
‫ouvir em um fluxo legível, que é o evento de erro.
‫Tão legível. on ('erro') e nesta função de retorno
‫de chamada, temos acesso ao objeto de erro.
‫Ok então neste caso iremos registrar este erro no console,
‫e então enviaremos o resultado do arquivo não encontrado.
‫E também podemos definir o código de status para um
‫erro, então 500, por exemplo. Normalmente, é definido automaticamente como
‫200, o que significa que está tudo bem, mas, neste caso, temos um erro de
‫servidor, o que significa que queremos enviar de volta o código 500.
‫Tudo bem, então vamos agora ...
‫Na verdade, preciso parar com isso novamente.
‫Então vamos ver o que acontece agora.
‫Ah, já temos um erro bem
‫aqui, res. o status não é uma função. E sim,
‫na verdade é statusCode. Portanto, esta é a
‫maneira que você escreve no expresso, então eu já estou acostumado a escrever
‫tanto no expresso, então expresso é um nó. js framework que vamos usar no
‫resto do curso e
‫expressamente você faz assim, então esse era
‫o problema. Então, já estou um pouco acostumado
‫a escrever expresso.
‫Vamos voltar e agora vemos o arquivo não encontrado, e se abrirmos
‫as ferramentas de desenvolvimento, você verá nosso código de erro 500 que acabamos de
‫enviar e, se formos para a guia da rede, vamos tentar
‫novamente, você tem o código de status aqui também.
‫Assim como vimos em um dos vídeos anteriores em outra
‫seção, na verdade. É assim que podemos
‫inspecionar esse tipo de coisa na guia rede.
‫Tudo bem, então vamos consertar aqui, salvá-lo, e tudo bem,
‫isso novamente funciona perfeitamente, mas ainda há um problema com
‫essa abordagem que acabei de mostrar
‫a vocês. E o problema
‫é que nosso fluxo legível, então aquele que
‫estamos usando para ler o arquivo do disco, é muito mais
‫rápido do que enviar o resultado com o fluxo
‫gravável de resposta pela rede. E isso sobrecarregará o fluxo
‫de resposta, que não pode lidar com todos esses dados recebidos tão rapidamente.
‫E esse problema é chamado de contrapressão.
‫E é um problema real que pode acontecer em situações reais.
‫Portanto, neste caso, a contrapressão ocorre quando a resposta
‫não consegue enviar os dados tão rápido quanto os está
‫recebendo do arquivo, isso faz sentido?
‫Portanto, temos que consertar essa
‫solução e encontrar uma ainda melhor.
‫E então, vamos criar a solução
‫três, e essa é realmente
‫a última, certo? Portanto, não há mais soluções do que três.
‫Então, o segredo aqui é realmente usar aquele operador de pipe
‫que mencionei no último vídeo, certo?
‫Portanto, o operador pipe está disponível em todos os streams legíveis e
‫nos permite canalizar a saída de um stream
‫legível diretamente para a entrada de um stream gravável, certo?
‫E isso resolverá o problema de contrapressão, porque
‫controlará automaticamente a velocidade basicamente dos dados
‫que entram e a velocidade dos dados que saem.
‫Ok, então vamos pegar nosso stream legível aqui.
‫Ok, então esse é nosso fluxo legível e
‫agora tudo o que temos a fazer é pegar nosso fluxo legível,
‫usar o método pipe nele e, em seguida, colocar um fluxo gravável e
‫essa é a resposta. E é isso mesmo.
‫É tudo o que precisamos fazer para essa solução, certo?
‫Então sempre funciona assim, deixe-me
‫escrever aqui como um comentário. Basicamente, precisamos de uma fonte legível, ok,
‫de novo, isso é apenas para explicar a você,
‫então podemos usar o pipe nela, e aqui teremos
‫que colocar um destino gravável. Portanto, é daí que
‫nossos dados vêm, e deve ser um
‫fluxo legível, e esses dados podemos canalizar para um destino gravável.
‫Então, neste caso, nosso destino é a resposta, certo?
‫Agora, este stream aqui pode ser um duplex ou um stream
‫de transformação também, mas o que importa é que
‫podemos escrever no stream. E a resposta,
‫claro, é esse fluxo. Portanto, podemos escrever para
‫a resposta que será enviada ao cliente, certo?
‫Assim, o operador de tubo resolve automaticamente o
‫problema de contrapressão que tínhamos anteriormente.
‫E também é uma solução muito mais
‫elegante e direta. Então, o que fizemos
‫aqui antes foi apenas mostrar a vocês todas as
‫maneiras pelas quais podemos usar os métodos e eventos de fluxo para criar
‫esse tipo de solução e, claro, eles têm muitos casos
‫de uso, mas em um problema como este, o tubo operador
‫é realmente a melhor solução. Nos bastidores, ele faz algo
‫assim aqui, na verdade, mas
‫novamente de uma maneira muito mais direta para
‫escrevermos, porque lida com todas as
‫coisas internamente nos bastidores. Portanto, acredito que o pipe
‫aqui seja na verdade a maneira mais fácil de consumir e
‫escrever streams, a menos que, claro, como mencionei, precisemos
‫de soluções mais customizadas. E então, temos
‫que usar essas ferramentas mais complicadas, como esses eventos
‫e métodos que mostrei antes. Tudo bem, então
‫para terminar, vamos encerrar o processo aqui, reiniciá-lo e, claro,
‫ver se ainda funciona, o que está funcionando.
‫E assim, nosso trabalho é feito aqui. Portanto, terminamos esta palestra e
‫estamos prontos para ir direto para a próxima.