NestJS+Fastify文件上传问题:@fastify/multipart使用及最佳方案咨询
NestJS + Fastify 文件上传问题解决与最佳实践
问题描述
- Multer无法处理非
multipart/form-data格式的数据,且与FastifyAdapter不兼容; - 使用
@fastify/multipart时,上传3个文件未触发预期报错,且文件大小限制设置无效。
尝试过的代码
async uploadFile(req: FastifyRequest, res: FastifyReply<any>): Promise<any> { // upload file try { //Check request is multipart if (!req.isMultipart()) { res.send( new BadRequestException( new AppResponseDto(400, undefined, 'Request is not multipart'), ), ); return; } // const options = { limits: { fieldSize: 1, // Max field value size in bytes }, }; const mp = await req.multipart(handler, onEnd, options); // for key value pairs in request mp.on('file', function (key: any, value: any) { // console.log('form-data', key, value,'<'); }); //Save files in directory async function handler( field: string, file: any, filename: string, encoding: string, mimetype: string, ): Promise<void> { const pipeline = util.promisify(stream.pipeline); const writeStream = fs.createWriteStream(`uploads/${filename}`); //File path try { await pipeline(file, writeStream); } catch (err) { console.error('Pipeline failed', err); } } // Uploading finished async function onEnd(err: any) { if (err) { res.send(new HttpException('Internal server error', 500)); return; } res .code(200) .send( new AppResponseDto(200, undefined, 'Data uploaded successfully'), ); } } catch (err) { console.log(err); } }
最佳实现方案
1. 全局注册@fastify/multipart插件
首先在main.ts中正确注册插件并配置全局限制,这是文件大小、数量限制生效的基础:
import { NestFactory } from '@nestjs/core'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import fastifyMultipart from '@fastify/multipart'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter()); // 注册multipart插件,配置全局限制 await app.register(fastifyMultipart, { limits: { fileSize: 1024 * 1024, // 单文件最大1MB files: 2, // 最多允许上传2个文件,超出自动报错 fieldSize: 1024, // 表单字段值最大1KB }, }); await app.listen(3000); } bootstrap();
2. 修正路由处理逻辑
之前的代码存在参数顺序错误,导致限制配置不生效。调整后的路由函数完善了错误捕获逻辑:
import { Controller, Post, HttpException, HttpStatus } from '@nestjs/common'; import { FastifyRequest, FastifyReply } from '@nestjs/platform-fastify'; import * as fs from 'fs'; import * as stream from 'stream'; import * as util from 'util'; import { AppResponseDto } from './app-response.dto'; const pipeline = util.promisify(stream.pipeline); @Controller('upload') export class UploadController { async uploadFile(req: FastifyRequest, res: FastifyReply): Promise<void> { try { if (!req.isMultipart()) { return res.status(HttpStatus.BAD_REQUEST).send( new AppResponseDto(400, undefined, '请求不是multipart格式'), ); } // 修正参数顺序:文件处理回调 -> 配置选项 -> 结束回调 const mp = await req.multipart( // 文件处理回调 async (field: string, file: any, filename: string, encoding: string, mimetype: string) => { // 可选:添加文件类型校验 const allowedTypes = ['image/jpeg', 'image/png']; if (!allowedTypes.includes(mimetype)) { throw new HttpException('仅支持JPG/PNG格式', HttpStatus.BAD_REQUEST); } const writeStream = fs.createWriteStream(`uploads/${filename}`); try { await pipeline(file, writeStream); } catch (err) { console.error('文件写入失败:', err); throw new HttpException('文件保存失败', HttpStatus.INTERNAL_SERVER_ERROR); } }, // 路由级限制(可覆盖全局配置) { limits: { fileSize: 1024 * 1024, }, }, // 上传结束回调,捕获插件抛出的限制错误 (err: any) => { if (err) { console.error('上传错误:', err); return res.status(HttpStatus.BAD_REQUEST).send( new AppResponseDto(400, undefined, err.message || '文件上传失败'), ); } return res.status(HttpStatus.OK).send( new AppResponseDto(200, undefined, '文件上传成功'), ); }, ); // 监听表单字段(如果需要处理非文件参数) mp.on('field', (key, value) => { console.log('表单字段:', key, value); }); } catch (err) { console.error('控制器错误:', err); res.status(HttpStatus.INTERNAL_SERVER_ERROR).send( new AppResponseDto(500, undefined, '服务器内部错误'), ); } } }
3. 关键问题修复点
- 限制不生效:之前
req.multipart的参数顺序错误,options应放在第二个位置;需使用fileSize(单文件大小)和files(文件数量)配置限制,而非仅fieldSize。 - 多文件不报错:当文件数量超出
limits.files时,插件会自动抛出错误,需在onEnd回调中捕获并返回对应响应。 - Multer兼容性:Fastify官方推荐用
@fastify/multipart替代Multer,后者基于Express开发,与Fastify适配器存在兼容性问题,且不支持非multipart/form-data的二进制流上传。
4. 支持二进制流上传(非multipart格式)
如果需要接收二进制格式的文件上传,可直接读取请求流:
@Post('binary') async binaryUpload(req: FastifyRequest, res: FastifyReply): Promise<void> { const writeStream = fs.createWriteStream('uploads/binary-file'); try { await pipeline(req.raw, writeStream); res.status(HttpStatus.OK).send(new AppResponseDto(200, undefined, '二进制文件上传成功')); } catch (err) { console.error('二进制上传失败:', err); res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(new AppResponseDto(500, undefined, '上传失败')); } }
内容的提问来源于stack exchange,提问作者Ihor Havryliak
相关产品推荐
相关产品推荐

