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

无需body-parser,如何在Express(NestJS)中增大请求体大小限制?

问题描述

现有一个基于Express的NestJS应用,采用流式方式处理文件上传:

  • Node.js端通过读取文件流并使用Axios发送:
const data = fs.createReadStream(filePath);
await axios.post('/files/upload', data)
  • NestJS端通过req对象获取传入的文件流,再将该流传入S3的PutObjectCommand完成上传:
@Post('/upload')
async upload(@Req() req: Request) {
  await s3Client.send(new PutObjectCommand({
    Bucket: 'bucket-name',
    Key: 'file-key',
    Body: req
  }));
}

上传大文件时,NestJS抛出错误:[ExceptionsHandler] request entity too large PayloadTooLargeError: request entity too large。

尝试添加express.urlencoded({ limit: '5gb', parameterLimit: Infinity })中间件虽解决了错误,但导致应用内存占用过高——原因是该中间件会解析请求体并将文件内容以缓冲区形式存入req.body。

咨询:能否在不解析请求体并存入req.body的前提下增大请求体大小限制?或是否有其他更合适的S3文件上传方案?


解决方案

一、不解析请求体调整大小限制

直接用Express的raw中间件配置大小限制即可,它只会校验请求体大小,不会解析内容到req.body,完全保留原始流:

全局配置(main.ts)

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as express from 'express';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 全局设置请求体大小限制,不解析内容
  app.use(express.raw({
    limit: '5gb',
    type: '*/*' // 匹配所有请求类型
  }));
  await app.listen(3000);
}
bootstrap();

单路由配置

如果不想全局生效,也可以给目标路由单独应用中间件:

import { Controller, Post, Req, UseMiddleware } from '@nestjs/common';
import * as express from 'express';

@Controller('files')
export class FilesController {
  @Post('/upload')
  @UseMiddleware(express.raw({ limit: '5gb', type: '*/*' }))
  async upload(@Req() req: Request) {
    await s3Client.send(new PutObjectCommand({
      Bucket: 'bucket-name',
      Key: 'file-key',
      Body: req
    }));
  }
}

二、更优的S3大文件上传方案

对于大文件,S3官方推荐分片上传(Multipart Upload),优势包括:断点续传、内存占用低、支持并发上传,能彻底解决大文件上传的内存和稳定性问题。

方案1:手动实现分片上传

  1. 初始化上传:获取S3分配的上传ID
@Post('/upload/init')
async initMultipartUpload() {
  const command = new CreateMultipartUploadCommand({
    Bucket: 'bucket-name',
    Key: 'file-key'
  });
  const response = await s3Client.send(command);
  return { uploadId: response.UploadId };
}
  1. 分片上传:客户端将文件分成固定大小(如5MB)的分片,并发上传并记录每个分片的ETag
// 客户端Node.js示例
const chunkSize = 5 * 1024 * 1024; // 5MB分片
const fileStream = fs.createReadStream(filePath, { highWaterMark: chunkSize });
let partNumber = 1;
const parts = [];

for await (const chunk of fileStream) {
  const command = new UploadPartCommand({
    Bucket: 'bucket-name',
    Key: 'file-key',
    UploadId: uploadId,
    PartNumber: partNumber,
    Body: chunk
  });
  const response = await s3Client.send(command);
  parts.push({ PartNumber: partNumber, ETag: response.ETag });
  partNumber++;
}
  1. 完成上传:所有分片上传后,通知S3合并分片
@Post('/upload/complete')
async completeMultipartUpload(@Body() body: { uploadId: string, parts: any[] }) {
  const command = new CompleteMultipartUploadCommand({
    Bucket: 'bucket-name',
    Key: 'file-key',
    UploadId: body.uploadId,
    MultipartUpload: { Parts: body.parts }
  });
  await s3Client.send(command);
}

方案2:使用AWS SDK工具类自动处理分片

AWS提供的@aws-sdk/lib-storage包中的Upload类,会自动判断文件大小,超过阈值就启用分片上传,无需手动处理分片逻辑:

import { Upload } from '@aws-sdk/lib-storage';

@Post('/upload')
async upload(@Req() req: Request) {
  const upload = new Upload({
    client: s3Client,
    params: {
      Bucket: 'bucket-name',
      Key: 'file-key',
      Body: req
    },
    partSize: 5 * 1024 * 1024, // 5MB分片大小
    queueSize: 4 // 并发上传的分片数
  });

  await upload.done();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:22:35