Upload de imagem com o Node.js

Giuliana Bezerra
Desenvolvimento com Node.js
5 min readMay 31, 2019

Quem nunca precisou desenvolver um sistema que suporte upload de imagem? Um exemplo comum é permitir ao usuário que adicione um avatar dele no sistema. Eu tive essa necessidade recentemente e encontrei algumas dificuldades para desenvolver essa feature simples. Existem várias soluções para esse problema, e por isso tive o trabalho de avaliar cada uma delas. Com base nas minhas pesquisas, resolvi elaborar esse post para apresentar uma solução que eu julgo a mais adequada. Iremos construir um serviço de upload de avatares e um frontend simples para testar esse upload.

Se você ainda não leu o meu post sobre desenvolvimento de serviços com o Node.js, recomendo a leitura antes de prosseguir.

Disclaimer: Por questões de simplicidade, nesse post o upload será para um storage local e não um de terceiros (amazon, cloudinary…). Apesar disso, a solução poderá ser facilmente adaptada para esses storages.

Requisitos

O leitor deve estar familiarizado com a construção de serviços com o Node.js e Javascript.

Configuração

É necessário instalar o Node.js e o Visual Studio Code (pode ser outro editor) para prosseguir.

Criação do Projeto Backend

Vamos criar um projeto chamado Upload Avatar Backend, que irá conter apenas o serviço de upload de avatar. Para isso, vamos definir a seguinte estrutura:

Estrutura do projeto Upload Avatar Backend.

Vamos utilizar as dependências básicas de um projeto Node.js com Express, e uma dependência nova, que será discutida mais a frente:

O arquivo server.js irá apenas chamar o app.js:

O app.js irá de fato iniciar o nosso serviço. O que é necessário fazer é apenas definir a rota do serviço de upload que iremos construir:

💡 A habilitação do CORS é necessária para o nosso teste local pois tanto o frontend como o backend estarão rodando na mesma máquina, com o mesmo IP. Dessa forma, é necessário desabilitar essa segurança para testar a nossa aplicação. No ambiente produtivo, se as aplicações precisassem ficar na mesma máquina (incomum) seria adicionado o IP da máquina ao invés do * no header Access-Control-Allow-Origin.

Para configurar a rota, vamos adicionar na pasta routes o arquivo index.js:

Foi definida uma rota para /api, então precisamos adicionar dentro de routes a pasta api com o arquivo index.js:

Como o recurso manipulado será um avatar, vamos criar dentro de api uma pasta avatar que irá conter um arquivo index.js que configura a operação de upload:

💡A boa prática aqui é apenas chamar um controller com a operação, separando assim a lógica da operação da definição da rota.

A operação será definida no controller de gravação, que deverá ser criado em /controllers/avatar/record.js:

Aqui é que mora a solução de fato. Para armazenar os arquivos, vamos utilizar a biblioteca multer, que sabe lidar com dados binários de imagem (multipart/form-data). No multer, tudo que você precisa é informar o diretório onde as imagens serão armazenadas, no meu caso eu escolhi o public/uploads. O single vai receber o formulário contendo a imagem, com o identificador avatar e salvá-la no diretório de destino especificado. Para que o frontend consiga referenciar a imagem é retornada uma URL com a sua localização. Simples, não é?

O problema é que o servidor não está “servindo” a pasta que contém as imagens, ele apenas configura as rotas. Para adicionar esse configuração é necessária apenas uma linha no nosso arquivo app.js:

Com essa mudança, se quisermos acessar uma imagem do backend é necessário apenas utilizar o endereço http://localhost:7755/public/uploads/nomedaimagem.

💡Você deve ter se perguntado: por que o serviço não salvou a imagem no banco de dados?

Essa era uma estratégia comumente utilizada para manipular arquivos binários, mas essa solução se torna impraticável a longo prazo visto que o tamanho do arquivo é grande e isso prejudica o desempenho da base de dados. A estratégia ideal é salvar uma URL para a imagem e referenciá-la no frontend. Assim, apenas uma String é salva na base, o que não compromete o desempenho da aplicação.

Criação do Projeto Frontend

Essa é a parte mais simples e não é o foco desse artigo. O frontend criado aqui servirá apenas para testar a nossa solução de upload, do ponto de vista do cliente do serviço.

A estrutura do projeto é praticamente a mesma, com a diferença de não haver rotas:

Estrutura do projeto Upload Avatar Frontend.

A única dependência do projeto é o Express:

O arquivo server.js chama o app.js:

O app.js irá disponibilizar a página HTML que permitirá o upload do avatar:

Agora só falta criar a página index.html dentro da pasta public:

A página contém apenas o formulário de upload. A lógica do submit é disparada pelo listener que passa como payload para o nosso serviço backend um formulário do tipo multipart/form-data contendo o arquivo que foi carregado com o identificador avatar.

💡O identificador “avatar” deverá ser usado no backend pelo método single do multer. É importante que os identificadores estejam iguais, caso contrário o multer não irá encontrar o arquivo da imagem na requisição.

Testando

Suba os dois projetos com o comando npm start e acesse o endereço do frontend: http://localhost:7756:

Informe um arquivo e pressione Upload:

Veja que a imagem é carregada! Se você inspecionar a imagem (botão direito do mouse em cima da imagem -> Inspecionar), verá que o src está apontando para a URL gerada para o avatar, que está sendo provida pelo servidor backend:

Conclusão

Vimos nesse post como construir um serviço de upload de avatares/imagens usando o Node.js. Nosso serviço armazena a imagem localmente e disponibiliza a mesma através de uma URL.

É possível estender essa implementação utilizando outros tipos de armazenamento, que são suportados pelo multer, caso seja interessante utilizar um storage pago, por exemplo. O importante aqui foi mostrar o caso mais simples, para que o leitor seja capaz de adaptar a solução conforme desejado.

Se você gosta do meu conteúdo, não deixe de conferir o meu canal do Youtube, onde falo sobre desenvolvimento de software. Espero te ver por lá! 😉

Referências

--

--

Giuliana Bezerra
Desenvolvimento com Node.js

Solution Architect — Online Instructor — Youtuber at @giulianabezerra — Writer