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

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);
  }
}

关键优势

  1. 无中间存储:文件流直接从请求传递到S3,无需写入磁盘或内存,节省资源
  2. 符合Nest设计:S3服务完全独立,通过依赖注入使用,无需在装饰器中硬编码依赖
  3. 可复用性:自定义拦截器和存储引擎可在整个项目中复用

内容的提问来源于stack exchange,提问作者DanilMakarov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:05:33