Passer au contenu

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 :

Fenêtre de terminal
$ 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().

Exemple de téléchargement de fichier
@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 fichier
  • options : objet optionnel de type MulterOptions. 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é :

Pipe de validation de taille de fichier
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 :

Upload et validation de fichiers
@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 :

errorHttpStatusCodeLe code d’état HTTP à renvoyer en cas d’échec de tout validateur. La valeur par défaut est 400 (BAD REQUEST)
exceptionFactoryUne 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 :

Interface FileValidator
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 en bytes)
  • 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é :

Exemple avec FileParsePipe
@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 :

Utilisation de ParseFilePipeBuilder
@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-dessus
  • maxCount : nombre optionnel définissant le nombre maximum de fichiers à accepter
  • options : objet optionnel MulterOptions, comme décrit ci-dessus

Lors de l’utilisation de FilesInterceptor(), extrayez des fichiers de la request avec le décorateur @UploadedFiles().

Téléchargement d'un tableau de fichiers
@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é requise name 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-dessus
  • options : objet optionnel MulterOptions, comme décrit ci-dessus

Lors de l’utilisation de FileFieldsInterceptor(), extrayez des fichiers de la request avec le décorateur @UploadedFiles().

Téléchargement de fichiers multiples
@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().

Téléchargement de fichiers quelconques
@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.

Acceptation des données sans fichiers
@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.

Enregistrement des options par défaut
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 :

Configuration asynchrone avec 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.

Injection de services de configuration
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 :

Configuration avec une classe de configuration
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.

Classe de service de configuration Multer
@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.

Réutilisation d'un fournisseur existant
MulterModule.registerAsync({
imports: [ConfigModule],
useExisting: ConfigService,
});

Exemple

Un exemple fonctionnel est disponible ici.