DEV Community

Leonardo Minora
Leonardo Minora

Posted on

NestJS - criar um endpoint para upload de diversos arquivos

Informações gerais

objetivo

  • criar 1 endpoint para API de upload de múltiplos arquivos

notas de aula

sumário

  1. pegar o código do projeto anterior
  2. executar a API
  3. criar e configurar 1 endpoint para receber vários arquivos
  4. criar método no serviço para retornar as informações dos arquivos

para receber vários arquivos, o nestjs disponibiliza os seguintes interceptors:

  • FilesInterceptor - identifica um array de arquivos com o mesmo nome de campo do formulário;
  • FileFieldsInterceptor -
  • AnyFilesInterceptor -

1. pegar o código do projeto anterior

a nota de aula anterior é NestJS - Upload de 1 arquivo que criou um projeto javascript para uma API Rest usando nestjs e typescript.

pode utilizar o seu próprio código, ou baixar o zip ou fazer o clone do repositório github.

2. Executar a API

lembre de acessar a pasta do projeto!!!

antes de executar a api, lembrar de instalar as bibliotecas do projeto com o comando npm i.

lançar a api em modo desenvolvimento executando o comando npm com a opção run para executar o script start:dev.

[upload-api] $ npm run start:dev 
Enter fullscreen mode Exit fullscreen mode

a execução deverá produzir um resultado parecido com o console abaixo.

[11:58:35] Starting compilation in watch mode... [11:58:37] Found 0 errors. Watching for file changes. [Nest] 12346 - 15/09/2024, 11:58:38 LOG [NestFactory] Starting Nest application... [Nest] 12346 - 15/09/2024, 11:58:38 LOG [InstanceLoader] AppModule dependencies initialized +14ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [InstanceLoader] UploadModule dependencies initialized +1ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RoutesResolver] AppController {/}: +15ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RouterExplorer] Mapped {/, GET} route +3ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RoutesResolver] UploadController {/upload}: +0ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RouterExplorer] Mapped {/upload/exemplo-simples, POST} route +2ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [NestApplication] Nest application successfully started +4ms 
Enter fullscreen mode Exit fullscreen mode

3. criar e configurar 1 endpoint para receber vários arquivos

adicionar um endpoint no controller src/upload/upload.controller.ts, atualizando as importações, conforme código (diff) abaixo.

import {  Controller, Post, UploadedFile, ++ UploadedFiles,  UseInterceptors, } from '@nestjs/common'; --import { FileInterceptor } from '@nestjs/platform-express'; ++import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; import { ++ ApiBadRequestResponse,  ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags, } from '@nestjs/swagger'; import { UploadService } from './upload.service';  @Controller('upload') @ApiTags('upload') export class UploadController {  constructor(private readonly uploadService: UploadService) {}   @Post('exemplo-simples') @UseInterceptors(FileInterceptor('arquivo')) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { arquivo: { type: 'string', format: 'binary', }, }, }, }) @ApiOperation({ summary: 'Exemplo de upload de 1 arquivo qualquer' }) @ApiResponse({ status: 201, description: 'Arquivo enviado com sucesso.' }) @ApiResponse({ status: 400, description: 'Erro no envio do arquivo.' }) uploadArquivoSimples(@UploadedFile() arq: Express.Multer.File) { console.log(arq);   return this.uploadService.responderInformacaoArquivo(arq); }  ++ @Post('arquivos') ++ @UseInterceptors(FilesInterceptor('arquivos')) ++ @ApiConsumes('multipart/form-data') ++ @ApiBody({ ++ schema: { ++ type: 'object', ++ properties: { ++ arquivos: { ++ type: 'array', ++ items: { ++ type: 'string', ++ format: 'binary', ++ }, ++ }, ++ }, ++ }, ++ }) ++ @ApiResponse({ ++ status: 201, ++ description: 'Arquivo(s) enviado(s) com sucesso.', ++ }) ++ @ApiBadRequestResponse({ ++ status: 400, ++ description: 'Erro no envio de arquivos.', ++ }) ++ uploadArquivos(@UploadedFiles() arquivos: Array<Express.Multer.File>) { ++ return { ++ estado: 'ok', ++ data: { ++ quantidade: arquivos?.length, ++ }, ++ }; ++ } }  
Enter fullscreen mode Exit fullscreen mode

foi adicionado nas importações o interceptor FilesInterceptor e o decorator UploadedFiles responsáveis por interceptar e extrair vários arquivos em uma array nomeada como arquivos no formulário.

o decorator @Post configura o verdo POST e o path arquivos do endpoint.
enquanto o decorator @UseInterceptors configura o uso do FilesInterceptor para interceptar a requisitação HTTTP(S) a procura da variável arquivos dentro do formulário no corpo da mensagem.

as próximas linhas contém os decorators @Api (ApiConsumes, ApiBody, ApiResponse, e ApiBadRequestResponse) para configurar a documentação do swagger.

a próxima linha, uploadArquivos(@UploadedFiles() arquivos: Array<Express.Multer.File>) especifica o método uploadArquivos.
este método tem como parâmetro a variável arquivos que irá receber um array contendo itens cada tipados com Express.Multer.File.
o parâmetro arquivos também esta decorado com UploadedFiles que é responsável por extrair o array de arquivos da mensagem HTTP.

ao salvar, a api será relançada e o terminal onde ela esta sendo executada deverá ficar como o console abaixo.

[11:58:35] Starting compilation in watch mode... [11:58:37] Found 0 errors. Watching for file changes. [Nest] 12346 - 15/09/2024, 11:58:38 LOG [NestFactory] Starting Nest application... [Nest] 12346 - 15/09/2024, 11:58:38 LOG [InstanceLoader] AppModule dependencies initialized +14ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [InstanceLoader] UploadModule dependencies initialized +1ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RoutesResolver] AppController {/}: +15ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RouterExplorer] Mapped {/, GET} route +3ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RoutesResolver] UploadController {/upload}: +0ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [RouterExplorer] Mapped {/upload/exemplo-simples, POST} route +2ms [Nest] 12346 - 15/09/2024, 11:58:38 LOG [NestApplication] Nest application successfully started +4ms 
Enter fullscreen mode Exit fullscreen mode

para testar, use a documentação da api acessando o endereço http://localhost:3000/docs/
clique no botão Try it out, escolher alguns arquivos, e clicar em Execute.
a operação deve estar semelhante com a figura abaixo e o resultado após a execução com o json abaixo da figura.

Image description

{ "estado": "ok", "data": { "quantidade": 3 } } 
Enter fullscreen mode Exit fullscreen mode

4. criar método no serviço para retornar as informações dos arquivos

no arquivo src/upload/upload.service.ts crie o método responderInformacoesArquivos(arquivos: Array<Express.Multer.File>) e coloque a implementação conforme arquivo (diff) abaixo.

import { Injectable } from '@nestjs/common';  @Injectable() export class UploadService {  responderInformacaoArquivo(arquivo: Express.Multer.File) { return { estado: 'ok', dados: { nome: arquivo.originalname, tamanho: arquivo.size, mimetype: arquivo.mimetype, encode: arquivo.encoding, }, }; } ++ ++ responderInformacoesArquivos(arquivos: Array<Express.Multer.File>) { ++ const informacoes = arquivos.map((arquivo) => { ++ return { ++ nome: arquivo.originalname, ++ tamanho: arquivo.size, ++ mimetype: arquivo.mimetype, ++ encode: arquivo.encoding, ++ }; ++ }); ++ return { ++ estado: 'ok', ++ dados: { ++ quantidade: arquivos?.length, ++ arquivos: informacoes, ++ }, ++ }; ++ } } 
Enter fullscreen mode Exit fullscreen mode

após a modificação do service terá de modificar o controller no arquivo src/upload/upload.controller.ts conforme arquivo abaixo para chamar o método responderInformacoesArquivos.

import {  Controller, Post, UploadedFile, UploadedFiles, UseInterceptors, } from '@nestjs/common'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; import {  ApiBadRequestResponse, ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags, } from '@nestjs/swagger'; import { UploadService } from './upload.service';  @Controller('upload') @ApiTags('upload') export class UploadController {  constructor(private readonly uploadService: UploadService) {}   @Post('exemplo-simples') @UseInterceptors(FileInterceptor('arquivo')) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { arquivo: { type: 'string', format: 'binary', }, }, }, }) @ApiOperation({ summary: 'Exemplo de upload de 1 arquivo qualquer' }) @ApiResponse({ status: 201, description: 'Arquivo enviado com sucesso.' }) @ApiResponse({ status: 400, description: 'Erro no envio do arquivo.' }) uploadArquivoSimples(@UploadedFile() arq: Express.Multer.File) { console.log(arq);   return this.uploadService.responderInformacaoArquivo(arq); }   @Post('arquivos') @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { arquivos: { type: 'array', items: { type: 'string', format: 'binary', }, }, }, }, }) @ApiResponse({ status: 201, description: 'Arquivo(s) enviado(s) com sucesso.', }) @ApiBadRequestResponse({ status: 400, description: 'Erro no envio de arquivos.', }) @UseInterceptors(FilesInterceptor('arquivos')) uploadArquivos(@UploadedFiles() arquivos: Array<Express.Multer.File>) { -- return { -- estado: 'ok', -- dados: { -- quantidade: arquivos?.length, -- arquivos: informacoes, -- }, -- }; ++ return this.uploadService.responderInformacoesArquivos(arquivos);  } }  
Enter fullscreen mode Exit fullscreen mode

para testar, pode usar novamente a documentação da API conforme a figura anterior.
o resultado agora será parecido conforme o json abaixo.

{ "estado": "ok", "dados": { "quantidade": 3, "arquivos": [ { "nome": "README.md", "tamanho": 1373, "mimetype": "text/markdown", "encode": "7bit" }, { "nome": "package.json", "tamanho": 2020, "mimetype": "application/json", "encode": "7bit" }, { "nome": "Captura de tela de 2024-09-15 15-35-33.png", "tamanho": 44726, "mimetype": "image/png", "encode": "7bit" } ] } } 
Enter fullscreen mode Exit fullscreen mode

Referência e link

Top comments (0)