You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合并UseInterceptors与UploadedFile两个装饰器处理文件?

实现NestJS文件上传装饰器合并方案

要将@UseInterceptors(FileInterceptor)和@UploadedFile合并为单个参数装饰器,核心思路是自定义参数装饰器,自动为目标方法注册文件拦截器并获取上传文件。以下是具体实现步骤:

1. 自定义合并装饰器实现

创建一个参数装饰器ImageUpload,内置文件拦截器的配置逻辑,同时自动为方法添加拦截器元数据:

import { createParamDecorator, ExecutionContext, BadRequestException, Reflector } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { IMAGE_COMPRESS_CONFIG } from './your-config-path'; // 替换为你的配置文件路径

const { MEGABYTE, SIZE_NUM } = IMAGE_COMPRESS_CONFIG;
const reflector = new Reflector();

// 定义可选配置类型
interface ImageUploadOptions {
  fieldName: string;
  multerOptions?: Partial<import('multer').Options>;
}

export const ImageUpload = createParamDecorator((options: ImageUploadOptions, ctx: ExecutionContext) => {
  const { fieldName, multerOptions = {} } = options;
  const handler = ctx.getHandler();

  // 检查方法是否已注册对应拦截器,避免重复添加
  const existingInterceptors = reflector.get('__nest_interceptors__', handler) || [];
  const hasTargetInterceptor = existingInterceptors.some(
    (interceptorRef: any) => 
      interceptorRef.interceptor.name === 'FileInterceptor' && 
      interceptorRef.interceptor.options?.fieldName === fieldName
  );

  if (!hasTargetInterceptor) {
    // 创建带默认配置的文件拦截器
    const customInterceptor = FileInterceptor(fieldName, {
      limits: {
        fileSize: MEGABYTE * SIZE_NUM,
        ...multerOptions.limits
      },
      fileFilter: (req, file, callback) => {
        const allowedMimeTypes = ['image/jpeg', 'image/png', 'image/jpg', 'image/webp'];
        if (allowedMimeTypes.includes(file.mimetype)) {
          callback(null, true);
        } else {
          callback(new BadRequestException('Invalid file type! Only JPG, JPEG, PNG and WebP are allowed.'), false);
        }
      },
      ...multerOptions
    });

    // 手动添加拦截器元数据到目标方法
    Reflect.defineMetadata('__nest_interceptors__', [...existingInterceptors, { interceptor: customInterceptor, options: {} }], handler);
  }

  // 从请求中获取上传文件,与@UploadedFile逻辑一致
  const request = ctx.switchToHttp().getRequest();
  return request.file;
});

2. 使用方式

直接在方法参数上使用@ImageUpload即可,无需额外添加@UseInterceptors和@UploadedFile:

基础用法

async upload(@ImageUpload({ fieldName: 'file' }) file: Express.Multer.File) {
  console.log(file);
}

自定义配置用法

如果需要覆盖默认的文件大小、添加额外multer配置:

async upload(
  @ImageUpload({
    fieldName: 'file',
    multerOptions: {
      limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
      dest: './temp-uploads'
    }
  }) file: Express.Multer.File
) {
  console.log(file);
}

实现原理

  • 利用NestJS的元数据机制,在参数装饰器内部自动为目标方法注册FileInterceptor,避免手动添加@UseInterceptors
  • 复用@UploadedFile的核心逻辑,从请求对象中提取上传的文件
  • 添加重复拦截器校验,避免同一方法多次注册相同拦截器导致的异常

内容的提问来源于stack exchange,提问作者Nguyễn Phan Duy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 03:39:58