0Pricing
NestJS Enterprise Backend APIs · 课时

使用 Multer 拦截器处理多部分上传

接入 FileInterceptor 和 FilesInterceptor,以支持带大小限制的单文件和多文件上传。

使用 Multer 拦截器处理多部分上传 是 CoddyKit 上的免费 NestJS Enterprise Backend APIs 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 NestJS Enterprise Backend APIs 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 NestJS Enterprise Backend APIs 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

Why Multer for Uploads

HTTP file uploads use the multipart/form-data content type, which splits the request body into parts: text fields and binary file payloads separated by a boundary marker.

NestJS does not parse this format on its own. Instead it ships first-class wrappers around Multer, the de-facto Express middleware for multipart parsing. You consume Multer through interceptors rather than wiring middleware manually.

  • FileInterceptor — one file from a single field
  • FilesInterceptor — many files from one field
  • FileFieldsInterceptor — files across several named fields

This lesson focuses on the first two and on enforcing size limits.

Installing the Types

The interceptors live in @nestjs/platform-express, which is already present in a standard Nest app. You only need the Multer type definitions to type your handler parameters correctly.

Install the dev dependency so Express.Multer.File is recognized by TypeScript:

npm install -D @types/multer

// Now Express.Multer.File is available globally in TypeScript.
// It describes the in-memory/disk file object Multer attaches
// to the request, e.g. originalname, mimetype, size, buffer, path.

Single File with FileInterceptor

Bind FileInterceptor('field') with @UseInterceptors, where the string is the name of the form-data field carrying the file. Then read the parsed file with the @UploadedFile() decorator.

The decorated parameter is a single Express.Multer.File. Notice the parameter name in the form does not need to match the handler argument — only the interceptor's field string matters.

import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';

@Controller('avatars')
export class AvatarsController {
  @Post()
  @UseInterceptors(FileInterceptor('avatar'))
  upload(@UploadedFile() file: Express.Multer.File) {
    return {
      name: file.originalname,
      type: file.mimetype,
      size: file.size,
    };
  }
}

Memory vs Disk Storage

By default Multer uses memory storage: the whole file lands in file.buffer as a Buffer. That is convenient for forwarding to S3 or processing in-process, but large files can exhaust RAM.

For local persistence use disk storage, where Multer streams to a path and gives you file.path instead of a buffer. Pass options as the second argument of the interceptor.

import { diskStorage } from 'multer';
import { extname } from 'path';
import { randomUUID } from 'crypto';

export const imageStorage = diskStorage({
  destination: './uploads/images',
  filename: (_req, file, cb) => {
    const unique = randomUUID();
    cb(null, `${unique}${extname(file.originalname)}`);
  },
});

// Usage:
// @UseInterceptors(FileInterceptor('photo', { storage: imageStorage }))

Enforcing a Size Limit

Never trust client-declared sizes. Cap the bytes Multer will accept via the limits.fileSize option (in bytes). When a file exceeds it, Multer aborts and Nest surfaces a 413-style error before your handler runs.

Combine fileSize with files to also bound the count of files in a multi-upload.

import { FileInterceptor } from '@nestjs/platform-express';

const FIVE_MB = 5 * 1024 * 1024;

@Post('avatar')
@UseInterceptors(
  FileInterceptor('avatar', {
    limits: { fileSize: FIVE_MB },
  }),
)
upload(@UploadedFile() file: Express.Multer.File) {
  return { stored: file.originalname };
}

Computing Limits Safely

Express limits are expressed in raw bytes, which is easy to get wrong by an order of magnitude. A tiny pure helper keeps the math explicit and testable, and it runs anywhere with no framework.

Below, mb converts megabytes to bytes and we sanity-check a candidate upload size against a cap.

function mb(n: number): number {
  return n * 1024 * 1024;
}

function withinLimit(sizeBytes: number, capMb: number): boolean {
  return sizeBytes <= mb(capMb);
}

const FILE_CAP_MB = 5;
console.log('5MB in bytes:', mb(FILE_CAP_MB));
console.log(withinLimit(mb(4), FILE_CAP_MB));   // true
console.log(withinLimit(mb(6), FILE_CAP_MB));   // false
console.log(withinLimit(5_242_880, FILE_CAP_MB)); // true (exactly 5MB)

Multiple Files with FilesInterceptor

FilesInterceptor('field', maxCount, options) accepts several files sent under the same field name. Read them with @UploadedFiles(), which yields an array.

The maxCount argument is a hard ceiling on how many files Nest will collect; extras trigger an error. Pair it with limits.fileSize for per-file byte caps.

import { Controller, Post, UploadedFiles, UseInterceptors } from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';

@Controller('gallery')
export class GalleryController {
  @Post()
  @UseInterceptors(
    FilesInterceptor('photos', 10, {
      limits: { fileSize: 5 * 1024 * 1024 },
    }),
  )
  upload(@UploadedFiles() files: Express.Multer.File[]) {
    return files.map((f) => ({ name: f.originalname, size: f.size }));
  }
}

Filtering by MIME Type

Size limits do not stop the wrong file type. Use fileFilter to accept or reject each part as it streams. Call the callback with (null, true) to keep a file or with an error to reject it.

Rejecting with a BadRequestException produces a clean 400 instead of a generic failure.

import { BadRequestException } from '@nestjs/common';
import { Request } from 'express';

export function imageFileFilter(
  _req: Request,
  file: Express.Multer.File,
  cb: (error: Error | null, accept: boolean) => void,
) {
  const allowed = ['image/png', 'image/jpeg', 'image/webp'];
  if (!allowed.includes(file.mimetype)) {
    return cb(new BadRequestException('Only PNG, JPEG, or WebP allowed'), false);
  }
  cb(null, true);
}

Validating with ParseFilePipe

Nest's built-in ParseFilePipe validates the already-parsed file declaratively inside the handler. It composes validators like MaxFileSizeValidator and FileTypeValidator, returning a 422 when they fail.

This is complementary to Multer's limits: Multer guards the stream, the pipe guards your business rules and gives clearer error messages.

import {
  ParseFilePipe,
  MaxFileSizeValidator,
  FileTypeValidator,
  UploadedFile,
} from '@nestjs/common';

@Post('avatar')
@UseInterceptors(FileInterceptor('avatar'))
upload(
  @UploadedFile(
    new ParseFilePipe({
      validators: [
        new MaxFileSizeValidator({ maxSize: 5 * 1024 * 1024 }),
        new FileTypeValidator({ fileType: /(png|jpe?g|webp)$/ }),
      ],
    }),
  )
  file: Express.Multer.File,
) {
  return { ok: true, name: file.originalname };
}

Centralizing Config with a Factory

Repeating storage, limits, and filters on every route is error-prone. Extract a single MulterOptions object (or a factory) and reuse it. Enterprise apps often register defaults globally via MulterModule.register() and override per-route only when needed.

This keeps caps consistent and makes raising a limit a one-line change.

import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
import { diskStorage } from 'multer';
import { imageFileFilter } from './image-file.filter';

export const imageUploadOptions: MulterOptions = {
  storage: diskStorage({ destination: './uploads/images' }),
  limits: { fileSize: 5 * 1024 * 1024, files: 10 },
  fileFilter: imageFileFilter,
};

// @UseInterceptors(FilesInterceptor('photos', 10, imageUploadOptions))

Handling the Size-Limit Error

When limits.fileSize is exceeded, Multer throws an error whose code is 'LIMIT_FILE_SIZE'. By default Nest wraps it, but you can map it to a friendly payload with an exception filter so clients get a clear, consistent message.

Translating low-level Multer codes into HTTP responses is a hallmark of production-grade upload endpoints.

import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus } from '@nestjs/common';
import { MulterError } from 'multer';
import { Response } from 'express';

@Catch(MulterError)
export class MulterExceptionFilter implements ExceptionFilter {
  catch(err: MulterError, host: ArgumentsHost) {
    const res = host.switchToHttp().getResponse<Response>();
    const status =
      err.code === 'LIMIT_FILE_SIZE'
        ? HttpStatus.PAYLOAD_TOO_LARGE
        : HttpStatus.BAD_REQUEST;
    res.status(status).json({ statusCode: status, message: err.message });
  }
}

Quick Check

Test your understanding of choosing the right interceptor and reading its result.

Recap

You can now wire multipart uploads end to end in NestJS:

  • FileInterceptor('field', options) + @UploadedFile() for one file.
  • FilesInterceptor('field', maxCount, options) + @UploadedFiles() for many files under one field.
  • limits.fileSize (bytes) caps stream size; limits.files caps count; fileFilter rejects bad MIME types early.
  • diskStorage vs memory storage decides whether you get file.path or file.buffer.
  • ParseFilePipe with MaxFileSizeValidator/FileTypeValidator validates declaratively, and a MulterError filter maps LIMIT_FILE_SIZE to a clean 413.

Centralize these options in one factory so limits stay consistent across every upload route.

常见问题解答

「使用 Multer 拦截器处理多部分上传」课时是免费的吗?

是的 — 「使用 Multer 拦截器处理多部分上传」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 NestJS Enterprise Backend APIs 课程的其余内容,请升级到 CoddyKit PRO。 NestJS Enterprise Backend APIs 课程共包含 4 节课。

「使用 Multer 拦截器处理多部分上传」这节课中我会学到什么?

接入 FileInterceptor 和 FilesInterceptor,以支持带大小限制的单文件和多文件上传。 你通过在浏览器中直接运行的动手代码来练习 NestJS Enterprise Backend APIs,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 NestJS Enterprise Backend APIs 需要有经验吗?

无需任何先前经验。CoddyKit 上的 NestJS Enterprise Backend APIs 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「使用 Multer 拦截器处理多部分上传」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 NestJS Enterprise Backend APIs 课中编写并运行代码吗?

能。每节 NestJS Enterprise Backend APIs 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Multer 拦截器处理多部分上传
  2. 使用 StreamableFile 流式传输大型响应
  3. 使用预签名 URL 直传 S3
  4. 使用 Sharp 构建图像处理流程
← 返回 NestJS Enterprise Backend APIs