NestJS - criar um endpoint para upload de diversos arquivos
Leonardo Minora
Posted on September 15, 2024
Informações gerais
- aula com endpoint para upload de arquivos para os alunos de programação orientada a serviços, do 4o ano de infoweb, do CNAT-IFRN
- repositório de código
- código final branch 03-upload-arquivos-multiplos zip
objetivo
- criar 1 endpoint para API de upload de múltiplos arquivos
notas de aula
sumário
- pegar o código do projeto anterior
- executar a API
- criar e configurar 1 endpoint para receber vários arquivos
- 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
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
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,
++ },
++ };
++ }
}
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
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.
{
"estado": "ok",
"data": {
"quantidade": 3
}
}
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,
++ },
++ };
++ }
}
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);
}
}
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"
}
]
}
}
Referência e link
- notas de aula baseado nos tutoriais/documentações nestjs
Posted on September 15, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.