Last active
February 19, 2023 01:32
-
-
Save LuckyArdhika/a9ecf80486e97a4c57b7fe9a35b8214c to your computer and use it in GitHub Desktop.
Files Filtering In Nestjs With Decorator or DTO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Void Decorator, File Filter | |
import { createParamDecorator, ExecutionContext, BadRequestException, UseInterceptors } from '@nestjs/common'; | |
import { FileInterceptor } from '@nestjs/platform-express'; | |
// First Choice | |
export const FileLimiter = createParamDecorator((data: { maxSize: number, mimeType: string[] }, req) => { | |
const files = req.files; | |
const invalidFiles = []; | |
for (const file of files) { | |
if (file.size > data.maxSize) { | |
invalidFiles.push({ name: file.originalname, error: 'File size exceeds maximum limit.' }); | |
} else if (!data.mimeType.includes(file.mimetype.split('/')[1])) { | |
invalidFiles.push({ name: file.originalname, error: 'Invalid file type.' }); | |
} | |
} | |
if (invalidFiles.length > 0) { | |
throw new BadRequestException(invalidFiles); | |
} | |
}); | |
// Second choice (priority) | |
export const FilesFilter = (data: { maxSize: number, type: string[], count: number }) => { | |
return (target: object, key: string, descriptor: PropertyDescriptor) => { | |
const originalMethod = descriptor.value; | |
descriptor.value = async function(...args) { | |
const files = args[0] ?? args[1]; | |
if (!files || !Array.isArray(files)) throw new BadRequestException('No files were uploaded'); | |
const invalidFiles = []; | |
if (files.length > count) throw new BadRequestException(`Number of files must be less than or equal to ${count}`); | |
for (const file of files) { | |
if (file.size > data.maxSize) { | |
invalidFiles.push({ name: file.originalname, error: 'File size exceeds maximum limit.' }); | |
} else if (!data.type.includes(file.mimetype.split('/')[1])) { | |
invalidFiles.push({ name: file.originalname, error: 'Invalid file type.' }); | |
} | |
} | |
if (invalidFiles.length > 0) { | |
throw new BadRequestException(invalidFiles); | |
} | |
request.files = files; | |
return originalMethod.apply(this, args); | |
}; | |
UseInterceptors(FileInterceptor('files')) | |
return descriptor; | |
}; | |
}; | |
// Usage // maxFiles: 10, maxSize/file: 1Mb, mimeType: contains | |
@FilesFilter({count: 10, maxSize: 100000, type: ['jpg','png','pdf']}) | |
@Post('/uploads') | |
// ########################################################################################################################## | |
*Or using DTO* | |
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | |
import { IsString, IsOptional, IsNumber, Max, Min, Matches } from 'class-validator'; | |
// Single File | |
export class FileUploadDto { | |
@ApiProperty({ type: 'string', format: 'binary' }) | |
@IsString() | |
@IsOptional() | |
file: any; | |
@ApiPropertyOptional() | |
@IsString() | |
@IsOptional() | |
filename?: string; | |
@ApiPropertyOptional() | |
@IsNumber() | |
@IsOptional() | |
@Min(1) | |
@Max(10 * 1024 * 1024) // 10MB in bytes | |
size?: number; | |
@ApiPropertyOptional() | |
@IsString() | |
@IsOptional() | |
@Matches(/image\/(jpeg|png)/, { | |
message: 'Only JPG and PNG files are allowed', | |
}) | |
mimetype?: string; | |
} | |
// Files | |
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | |
import { IsString, IsOptional, IsNumber, Max, Min, Matches } from 'class-validator'; | |
export class FileUploadDto { | |
@ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } }) | |
@IsString({ each: true }) | |
@IsOptional() | |
files: any[]; | |
@ApiPropertyOptional() | |
@IsString() | |
@IsOptional() | |
filename?: string[]; | |
@ApiPropertyOptional() | |
@IsNumber({}, { each: true }) | |
@IsOptional() | |
@Min(1) | |
@Max(10 * 1024 * 1024) // 10MB in bytes | |
size?: number[]; | |
@ApiPropertyOptional() | |
@IsString({ each: true }) | |
@IsOptional() | |
@Matches(/image\/(jpeg|png)/, { | |
each: true, | |
message: 'Only JPG and PNG files are allowed', | |
}) | |
mimetype?: string[]; | |
} | |
// Controller (Ussing DTO) | |
@Post() | |
@ApiConsumes('multipart/form-data') | |
async uploadFiles(@UploadedFiles() files: Express.Multer.File[], fileUploadDto: FileUploadDto) { | |
await this.fileUploadService.validateFileUploadDto(fileUploadDto); | |
// Process the uploaded files | |
// ... | |
} | |
// Service | |
export class FileUploadService { | |
async validateFileUploadDto(fileUploadDto: FileUploadDto) { | |
const errors = await validate(fileUploadDto); | |
if (errors.length > 0) { | |
throw new BadRequestException(errors); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment