Téléchargement de fichiers
Pour gérer le téléchargement de fichiers, Nest fournit un module intégré basé sur le package middleware multer pour Express. Multer gère les données publiées au format multipart/form-data
, qui est principalement utilisé pour télécharger des fichiers via une requête HTTP POST
. Ce module est entièrement configurable et vous pouvez ajuster son comportement selon les exigences de votre application.
Pour une meilleure sécurité des types, installons le package de typage Multer :
$ npm i -D @types/multer
Avec ce package installé, nous pouvons maintenant utiliser le type Express.Multer.File
(vous pouvez importer ce type comme suit : import { Express } from 'express'
).
Exemple de base
Pour télécharger un seul fichier, il suffit de lier l’intercepteur FileInterceptor()
au gestionnaire de route et d’extraire file
de la request
à l’aide du décorateur @UploadedFile()
.
@Post('upload')@UseInterceptors(FileInterceptor('file'))uploadFile(@UploadedFile() file: Express.Multer.File) { console.log(file);}
Le décorateur FileInterceptor()
prend deux arguments :
fieldName
: chaîne qui fournit le nom du champ du formulaire HTML qui contient un fichieroptions
: objet optionnel de typeMulterOptions
. C’est le même objet utilisé par le constructeur multer (plus de détails ici).
Validation de fichiers
Il peut souvent être utile de valider les métadonnées du fichier entrant, comme la taille des fichiers ou le type mime du fichier. Pour cela, vous pouvez créer votre propre Pipe et le lier au paramètre annoté avec le décorateur UploadedFile
. L’exemple ci-dessous montre comment un pipe de validation de taille de fichier de base pourrait être implémenté :
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()export class FileSizeValidationPipe implements PipeTransform { transform(value: any, metadata: ArgumentMetadata) { // "value" est un objet contenant les attributs et les métadonnées du fichier const oneKb = 1000; return value.size < oneKb; }}
Nest fournit un pipe intégré pour gérer les cas d’utilisation courants et faciliter/standardiser l’ajout de nouveaux. Ce pipe s’appelle ParseFilePipe
, et vous pouvez l’utiliser comme suit :
@Post('file')uploadFileAndPassValidation( @Body() body: SampleDto, @UploadedFile(new ParseFilePipe({ validators: [ // ... Ensemble d'instances de validateurs de fichiers ici ] })) file: Express.Multer.File,) { return { body, file: file.buffer.toString(), };}
Comme vous pouvez le voir, il est nécessaire de spécifier un tableau de validateurs de fichiers qui seront exécutés par le ParseFilePipe
. Nous discuterons de l’interface d’un validateur, mais il convient de mentionner que ce pipe dispose également de deux options supplémentaires facultatives :
errorHttpStatusCode | Le code d’état HTTP à renvoyer en cas d’échec de tout validateur. La valeur par défaut est 400 (BAD REQUEST) |
---|---|
exceptionFactory | Une fabrique qui reçoit le message d’erreur et renvoie une erreur. |
Revenons maintenant à l’interface FileValidator
. Pour intégrer des validateurs avec ce pipe, vous devez soit utiliser des implémentations intégrées, soit fournir votre propre FileValidator
. Voici l’exemple ci-dessous :
export abstract class FileValidator<TValidationOptions = Record<string, any>> { constructor(protected readonly validationOptions: TValidationOptions) {}
/** * Indique si ce fichier doit être considéré comme valide, selon les options passées dans le constructeur. * @param file le fichier de l'objet request */ abstract isValid(file?: any): boolean | Promise<boolean>;
/** * Construit un message d'erreur en cas d'échec de la validation. * @param file le fichier de l'objet request */ abstract buildErrorMessage(file: any): string;}
FileValidator
est une classe régulière qui a accès à l’objet fichier et le valide selon les options fournies par le client. Nest dispose de deux implémentations intégrées de FileValidator
que vous pouvez utiliser dans votre projet :
MaxFileSizeValidator
- Vérifie si la taille d’un fichier donné est inférieure à la valeur fournie (mesurée enbytes
)FileTypeValidator
- Vérifie si le type mime d’un fichier donné correspond à la valeur donnée.
Pour comprendre comment ces validateurs peuvent être utilisés en conjonction avec le FileParsePipe
mentionné précédemment, nous allons utiliser un extrait modifié du dernier exemple présenté :
@UploadedFile(new ParseFilePipe({ validators: [ new MaxFileSizeValidator({ maxSize: 1000 }), new FileTypeValidator({ fileType: 'image/jpeg' }), ],}))file: Express.Multer.File,
Enfin, vous pouvez utiliser la classe spéciale ParseFilePipeBuilder
qui vous permet de composer et de construire vos validateurs. En l’utilisant comme illustré ci-dessous, vous pouvez éviter l’instanciation manuelle de chaque valideur et simplement passer leurs options directement :
@UploadedFile(new ParseFilePipeBuilder() .addFileTypeValidator({ fileType: 'jpeg' }) .addMaxSizeValidator({ maxSize: 1000 }) .build({ errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }),)file: Express.Multer.File,
Tableau de fichiers
Pour télécharger un tableau de fichiers (identifiés par un seul nom de champ), utilisez le décorateur FilesInterceptor()
(notez le pluriel Files dans le nom du décorateur). Ce décorateur prend trois arguments :
fieldName
: comme décrit ci-dessusmaxCount
: nombre optionnel définissant le nombre maximum de fichiers à accepteroptions
: objet optionnelMulterOptions
, comme décrit ci-dessus
Lors de l’utilisation de FilesInterceptor()
, extrayez des fichiers de la request
avec le décorateur @UploadedFiles()
.
@Post('upload')@UseInterceptors(FilesInterceptor('files'))uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) { console.log(files);}
Fichiers multiples
Pour télécharger plusieurs fichiers (tous avec des clés de noms de champ différents), utilisez le décorateur FileFieldsInterceptor()
. Ce décorateur prend deux arguments :
uploadedFields
: un tableau d’objets, où chaque objet spécifie une propriété requisename
avec une valeur chaîne spécifiant un nom de champ, comme décrit ci-dessus, et une propriétémaxCount
optionnelle, comme décrite ci-dessusoptions
: objet optionnelMulterOptions
, comme décrit ci-dessus
Lors de l’utilisation de FileFieldsInterceptor()
, extrayez des fichiers de la request
avec le décorateur @UploadedFiles()
.
@Post('upload')@UseInterceptors(FileFieldsInterceptor([ { name: 'avatar', maxCount: 1 }, { name: 'background', maxCount: 1 },]))uploadFile(@UploadedFiles() files: { avatar?: Express.Multer.File[], background?: Express.Multer.File[] }) { console.log(files);}
Tous les fichiers
Pour télécharger tous les champs avec des clés de noms arbitraires, utilisez le décorateur AnyFilesInterceptor()
. Ce décorateur peut accepter un objet d’options facultatif comme décrit ci-dessus.
Lors de l’utilisation de AnyFilesInterceptor()
, extrayez des fichiers de la request
avec le décorateur @UploadedFiles()
.
@Post('upload')@UseInterceptors(AnyFilesInterceptor())uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) { console.log(files);}
Aucun fichier
Pour accepter multipart/form-data
mais ne pas permettre le téléchargement de fichiers, utilisez le NoFilesInterceptor
. Cela définit les données multiparties comme attributs dans le corps de la requête. Tous les fichiers envoyés avec la requête déclencheront une BadRequestException
.
@Post('upload')@UseInterceptors(NoFilesInterceptor())handleMultiPartData(@Body() body) { console.log(body);}
Options par défaut
Vous pouvez spécifier des options multer dans les intercepteurs de fichiers comme décrit ci-dessus. Pour définir des options par défaut, vous pouvez appeler la méthode statique register()
lorsque vous importez le MulterModule
, en passant les options prises en charge. Vous pouvez utiliser toutes les options énumérées ici.
MulterModule.register({ dest: './upload',});
Configuration asynchrone
Lorsque vous devez définir les options du MulterModule
de manière asynchrone plutôt que statique, utilisez la méthode registerAsync()
. Comme pour la plupart des modules dynamiques, Nest fournit plusieurs techniques pour gérer la configuration asynchrone.
Une technique consiste à utiliser une fonction de fabrique :
MulterModule.registerAsync({ useFactory: () => ({ dest: './upload', }),});
Comme d’autres fournisseurs de fabrique, notre fonction de fabrique peut être asynchrone et peut injecter des dépendances via inject
.
MulterModule.registerAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ dest: configService.get<string>('MULTER_DEST'), }), inject: [ConfigService],});
Alternativement, vous pouvez configurer le MulterModule
en utilisant une classe plutôt qu’une fabrique, comme indiqué ci-dessous :
MulterModule.registerAsync({ useClass: MulterConfigService,});
La construction ci-dessus instancie MulterConfigService
à l’intérieur du MulterModule
, en l’utilisant pour créer l’objet d’options requis. Notez que dans cet exemple, le MulterConfigService
doit implémenter l’interface MulterOptionsFactory
, comme indiqué ci-dessous.
@Injectable()class MulterConfigService implements MulterOptionsFactory { createMulterOptions(): MulterModuleOptions { return { dest: './upload', }; }}
Si vous souhaitez réutiliser un fournisseur d’options existant plutôt que de créer une copie privée à l’intérieur du MulterModule
, utilisez la syntaxe useExisting
.
MulterModule.registerAsync({ imports: [ConfigModule], useExisting: ConfigService,});
Exemple
Un exemple fonctionnel est disponible ici.