Obtendo o status de progresso do envio de dados com Axios

React 18 de Set de 2020

Introdução

Vamos mostrar na prática como obter o progresso de cada requisição HTTP sendo feita através do método POST, do front end para o back end utilizando o Axios.

Caso de uso

Imagine que você tem um app front end que faz upload de arquivos para um servidor. Você quer acompanhar e mostrar ao usuário o progresso do envio desses arquivos, como no exemplo abaixo:

Isso é possível e fácil de implementar utilizando o Axios.

Vamos como codar essa funcionalidade.

Mensurando o Progresso de Upload na Prática

Sintaxe básica:

	   // Utilizando axios
	   import axios from axios;

	   // Criando um FormData que armazena a imagem (arquivo)
	   const data = new FormData();
	   data.append("file", file, filename);
  
	   // Fazendo a requisição para o servidor com método POST
	   // Enviando o arquivo que está na variável data
	   // Passando um objeto de configuração que possui um método onUploadProgress
      axios
        .post("https://my.server.com/posts", data, {
          onUploadProgress: (event) => {
            let progress: number = Math.round(
              (event.loaded * 100) / event.total
            );

            console.log(
              `A imagem ${filename} está ${progress}% carregada... `
            );  
          },
        })
        .then((response) => {
          console.log(
            `A imagem ${filename} já foi enviada para o servidor!`
          );    
        })
        .catch((err) => {
          console.error(
            `Houve um problema ao realizar o upload da imagem ${filename} no servidor AWS`
          );
          console.log(err); 
        });

Basicamente é realizado uma requisição do tipo POST utilizando o Axios, passando a variável data, com o arquivo. Passamos também um objeto com a configuração da requisição. Nele contém o método onUploadProgress que recebe um event, esse event contém as propriedades loaded e total.

loaded é o quanto já foi carregado e total armazena o tamanho total do arquivo.

Então é feito um cálculo de regra de três para definir a percentagem que já foi carregada no servidor. A variável progress armazena esses valores conforme a função onUploadProgress é executada automaticamente.

Como estamos lidando com uma Promises, no final da execução o método Then é executado e conseguimos informar que o arquivo já foi enviado.

Se houver algum erro, o método Catch é executado, assim podemos imprimir no log o erro.

Até aqui entendemos a lógica de como funciona o progresso do envio de arquivos.

No código abaixo vamos explorar um pouco mais sobre o assunto, mostrando um trecho de código que foi utilizado no exemplo de caso de uso.

import api from "../services/api";

export interface IFile {
  id: string;
  name: string;
  readableSize: string;
  uploaded?: boolean;
  preview: string;
  file: File | null;
  progress?: number;
  error?: boolean;
  url: string;
}

const [uploadedFiles, setUploadedFiles] = useState<IFile[]>([]);

const updateFile = useCallback((id, data) => {
    setUploadedFiles((state) =>
      state.map((file) => (file.id === id ? { ...file, ...data } : file))
    );
  }, []);

const processUpload = useCallback(
    (uploadedFile: IFile) => {
      const data = new FormData();
      if (uploadedFile.file) {
        data.append("file", uploadedFile.file, uploadedFile.name);
      }

      api
        .post("posts", data, {
          onUploadProgress: (progressEvent) => {
            let progress: number = Math.round(
              (progressEvent.loaded * 100) / progressEvent.total
            );

            console.log(
              `A imagem ${uploadedFile.name} está ${progress}% carregada... `
            );

            updateFile(uploadedFile.id, { progress });
          },
        })
        .then((response) => {
          console.log(
            `A imagem ${uploadedFile.name} já foi enviada para o servidor!`
          );

          updateFile(uploadedFile.id, {
            uploaded: true,
            id: response.data._id,
            url: response.data.url,
          });
        })
        .catch((err) => {
          console.error(
            `Houve um problema ao fazer upload da imagem ${uploadedFile.name} no servidor AWS`
          );
          console.log(err);

          updateFile(uploadedFile.id, {
            error: true,
          });
        });
    },
    [updateFile]
  );

Nesse código, o evento de onUploadedFile é executado seguindo a mesma lógica que explicamos anteriormente, o progresso será calculado e a função updateFile é chamada a cada update do evento, recebendo o id do arquivo enviado ao servidor e o progresso.

A função updateFile irá alterar o estado do arquivo em questão, passando o valor atual do progresso que irá refletir na tela com um Loading de progresso usando a lib react-circular-progressbar. Conforme vimos no vídeo do caso de uso acima.

Quando é finalizado o upload do arquivo, o método then é executado e invoca a função updateFile, passando o id fake do arquivo e um objeto que tem o id que o servidor gerou e a URL do arquivo que já foi salva no S3 da AWS.

Na tela vai aparecer um link excluir e um ícone representará que o arquivo foi enviado com sucesso!

Algum erro pode acontecer na execução, o servidor não permite arquivos maiores que 2 MB.

Se o usuário enviar um arquivo maior que o limite estabelecido no servidor, o método catch será executado. Então iremos alterar o estado do uploadedFile com um error: true que irá alterar o ícone no front end, indicando que o arquivo que está no preview não foi enviado por algum motivo — poderíamos colocar um tooltip para mostrar o log do erro no componente ;)

E temos aqui o resultado esperado:

Imagem sendo enviados ao servidor, um deu erro, três estão sendo enviados e quatro foram enviados com sucesso! Nessa imagem podemos observar o progresso de cada envio 🚀

Conclusão

Objetivo foi mostrar essa funcionalidade bacana do Axios de ouvir o evento de envio de arquivos e mostrar a quantidade em percentual do processamento do envio, com isso podemos mostrar um loading bem legal.

Conseguimos melhorar a experiência do usuário mostrando o progresso do envio arquivo.

O objetivo não foi mostrar o fluxo completo do front end, isso vai ficar para um próximo post. Mas para não te deixar na curiosidade, indiquei um vídeo logo abaixo que é para você aprender toda a lógica dessa implementação!

Se você quer aprender a fazer o upload completo de arquivos em Front End e implementar a funcionalidade de progresso de envio de arquivos, acompanhe esse vídeo:

Observação: esse vídeo é antigo, usando React com classes. Mas vale a pena acompanhar a lógica para implementar esse tipo de funcionalidade, talvez você possa refatorar o projeto para React Hooks ;)

E aí, o que achou do post?

Espero que tenha curtido! 💜

O aprendizado é contínuo e sempre haverá um próximo nível! 🚀

Marcadores

Thiago Marinho

Dev Apaixonado por Tecnologia & Educação! Evolua rápido como a tecnologia, aprendizado é contínuo e sempre haverá um próximo nível. Boost Yourself! 🚀