Nest.js/Node.js基于Multer文件上传:如何传递文件流至处理器
解决方案:Nest.js中直接传递Multer文件流至处理器(无需存盘/内存)
核心思路
自定义Multer存储引擎,不将文件写入磁盘或内存,而是将原始可读流挂载到文件对象上;同时利用Nest.js的依赖注入机制,在路由处理器中注入独立的S3服务完成上传,完全符合Nest的模块化设计。
步骤1:实现自定义Multer存储引擎
创建一个不做持久化的存储引擎,仅保留文件流并暴露给后续处理器:
// src/common/storage/stream.storage.ts import { StorageEngine } from 'multer'; export class StreamStorage implements StorageEngine { // 处理文件上传:直接挂载原始流到file对象 _handleFile(req: any, file: any, callback: (error?: any, info?: Partial<Express.Multer.File>) => void) { file.stream = file.stream; callback(null, { filename: file.originalname, mimetype: file.mimetype, stream: file.stream // 关键:暴露可读流 }); } // 无需删除文件(未持久化) _removeFile(req: any, file: any, callback: (error: Error | null) => void) { callback(null); } }
步骤2:封装可复用的文件拦截器
为了避免重复配置,封装自定义的文件拦截器:
// src/common/interceptors/stream-file.interceptor.ts import { FileInterceptor } from '@nestjs/platform-express'; import { StreamStorage } from '../storage/stream.storage'; // 封装自定义拦截器,直接使用StreamStorage export const StreamFileInterceptor = (fieldName: string) => FileInterceptor(fieldName, { storage: new StreamStorage() });
步骤3:控制器中使用拦截器并注入S3服务
在路由处理器中直接获取文件流,调用独立的S3服务完成上传:
// src/upload/upload.controller.ts import { Controller, Post, UseInterceptors, UploadedFile, Inject } from '@nestjs/common'; import { StreamFileInterceptor } from '../common/interceptors/stream-file.interceptor'; import { S3Service } from '../s3/s3.service'; // 你的独立S3模块服务 @Controller('upload') export class UploadController { constructor(@Inject(S3Service) private readonly s3Service: S3Service) {} @Post() @UseInterceptors(StreamFileInterceptor('file')) async uploadFile(@UploadedFile() file: Express.Multer.File & { stream: NodeJS.ReadableStream }) { // 直接使用注入的S3服务上传流 const uploadResult = await this.s3Service.upload( file.stream, file.originalname, file.mimetype ); return { status: 'success', data: uploadResult }; } }
步骤4:独立S3服务实现
你的S3服务保持独立模块化,无需和Multer耦合:
// src/s3/s3.service.ts import { Injectable } from '@nestjs/common'; import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; import { ConfigService } from '@nestjs/config'; @Injectable() export class S3Service { private readonly s3Client: S3Client; private readonly bucketName: string; constructor(private readonly configService: ConfigService) { // 从配置中读取S3参数,符合Nest配置规范 this.s3Client = new S3Client({ region: this.configService.get('AWS_REGION'), credentials: { accessKeyId: this.configService.get('AWS_ACCESS_KEY_ID'), secretAccessKey: this.configService.get('AWS_SECRET_ACCESS_KEY') } }); this.bucketName = this.configService.get('AWS_S3_BUCKET'); } async upload(stream: NodeJS.ReadableStream, filename: string, mimetype: string) { const command = new PutObjectCommand({ Bucket: this.bucketName, Key: filename, Body: stream, ContentType: mimetype }); return await this.s3Client.send(command); } }
关键优势
- 无中间存储:文件流直接从请求传递到S3,无需写入磁盘或内存,节省资源
- 符合Nest设计:S3服务完全独立,通过依赖注入使用,无需在装饰器中硬编码依赖
- 可复用性:自定义拦截器和存储引擎可在整个项目中复用
内容的提问来源于stack exchange,提问作者DanilMakarov
相关产品推荐
相关产品推荐

