外观
文件上传模块
基于 NestJS + Fastify Multipart 的文件上传方案,支持 Zod Schema 验证、文件安全检查。
安装配置
1. 注册 Fastify Multipart 插件
typescript
// main.ts
import fastifyMultipart from '@fastify/multipart'
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
)
await app.register(fastifyMultipart)
await app.listen(3000)
}2. 导入 MultipartModule
typescript
import { MultipartModule } from '@maxtan/nest-core'
@Module({
imports: [
MultipartModule.forRoot({
maxFileSize: 20 * 1024 * 1024, // 20MB(默认)
maxFiles: 100, // 最大文件数(默认)
timeout: 30000 // 超时(默认 30s)
})
]
})
export class AppModule {}基础用法
typescript
import { Controller, Post } from '@nestjs/common'
import { Multipart, MultipartData, ParsedMultipart } from '@maxtan/nest-core'
@Controller('upload')
export class UploadController {
@Post('file')
@Multipart()
async uploadFile(@MultipartData() data: ParsedMultipart) {
const { fields, files } = data
const file = files[0]
console.log(file.filename) // 文件名
console.log(file.size) // 大小(字节)
console.log(file.mimetype) // MIME 类型
console.log(file.buffer) // Buffer 内容
return { success: true }
}
}使用 Zod Schema 验证表单字段
typescript
import { z } from 'zod'
const UploadSchema = z.object({
title: z.string().min(1, '标题不能为空'),
description: z.string().optional()
})
@Post('avatar')
@Multipart({ schema: UploadSchema, maxFileSize: 5 * 1024 * 1024 })
async uploadAvatar(@MultipartData() data: ParsedMultipart<z.infer<typeof UploadSchema>>) {
// data.fields — 已验证的表单数据
// data.files — 文件列表
return { title: data.fields.title }
}只获取文件或字段
typescript
// 只获取文件
@Post('files-only')
@Multipart()
async uploadFiles(@MultipartData('files') files: UploadedFile[]) {
return files.map(f => ({ name: f.filename, size: f.size }))
}
// 只获取字段
@Post('fields-only')
@Multipart({ schema: CreateSchema })
async createWithFields(@MultipartData('fields') fields: Create) {
return fields
}文件类型安全
MIME 类型白名单
typescript
import { UPLOAD_CONFIG } from '@maxtan/nest-core'
// 只允许图片
@Post('image')
@Multipart({
allowedMimeTypes: UPLOAD_CONFIG.ALLOWED_IMAGE_TYPES,
maxFileSize: 5 * 1024 * 1024
})
async uploadImage(@MultipartData() data: ParsedMultipart) {}
// 只允许文档
@Post('doc')
@Multipart({
allowedMimeTypes: UPLOAD_CONFIG.ALLOWED_DOCUMENT_TYPES,
maxFiles: 3
})
async uploadDoc(@MultipartData() data: ParsedMultipart) {}
// 自定义
@Post('custom')
@Multipart({
allowedMimeTypes: ['image/jpeg', 'application/pdf'],
forbiddenExtensions: ['.exe', '.bat']
})
async uploadCustom(@MultipartData() data: ParsedMultipart) {}内置常量
typescript
import { UPLOAD_CONFIG } from '@maxtan/nest-core'
UPLOAD_CONFIG.ALLOWED_IMAGE_TYPES
// ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml', 'image/bmp']
UPLOAD_CONFIG.ALLOWED_DOCUMENT_TYPES
// ['application/pdf', 'application/msword', ...]
UPLOAD_CONFIG.ALLOWED_ARCHIVE_TYPES
// ['application/zip', ...]
UPLOAD_CONFIG.FORBIDDEN_EXTENSIONS
// ['.exe', '.bat', '.cmd', '.sh', '.ps1', '.vbs', '.scr', '.dll', '.so', '.dylib', '.app']类型定义
UploadedFile
typescript
interface UploadedFile {
filename: string // 原始文件名
size: number // 大小(字节)
mimetype: string // MIME 类型
buffer: Buffer // 文件内容
fieldname: string // 表单字段名
encoding: string // 编码类型
}ParsedMultipart
typescript
interface ParsedMultipart<T = Record<string, any>> {
fields: T // 表单字段(验证后)
files: UploadedFile[] // 文件列表
}MultipartPipeOptions
typescript
interface MultipartPipeOptions {
maxFileSize?: number // 最大文件大小(字节),默认 20MB
maxFiles?: number // 最大文件数量,默认 100
timeout?: number // 超时(毫秒),默认 30000
allowedMimeTypes?: string[] // MIME 类型白名单
forbiddenExtensions?: string[] // 扩展名黑名单
}错误处理
模块自动抛出 BadRequestException:
| 场景 | 错误信息示例 |
|---|---|
| 文件大小超限 | 文件 example.jpg 超过大小限制, 最大 20.00MB |
| 文件数量超限 | 最多只能上传 100 个文件 |
| MIME 类型不允许 | 不支持的文件类型: application/x-msdownload |
| 扩展名禁止 | 禁止上传的文件类型: .exe |
| Schema 验证失败 | title: 标题不能为空 |
| 上传超时 | 文件上传超时, 最大 30 秒 |
| 非 multipart 请求 | 请求必须为 multipart/form-data 格式 |
注意事项
- 必须先注册
@fastify/multipart插件 - Buffer 模式:文件自动转为 Buffer,适合中小文件
- 内存限制:大文件建议设置合理的
maxFileSize - 全局拦截器:只处理带
@Multipart()装饰器的路由
最佳实践
- 始终配置 MIME 白名单:使用
allowedMimeTypes限制上传文件类型,不要允许任意文件 - 合理设置大小限制:头像 5MB、文档 20MB、视频 500MB,按场景区分
- 表单字段必须 Zod 校验:通过
schema选项传入 Zod Schema,确保字段安全 - 扩展名黑名单保留默认值:
UPLOAD_CONFIG.FORBIDDEN_EXTENSIONS已包含常见危险扩展名
相关文档
- 验证管道 (Zod) — 表单字段校验
- 异常过滤器 — 上传错误的响应格式