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

如何在Next.js中立即终止超出限制的文件上传?

解决Next.js中服务端立即终止超大文件上传的问题

你的问题核心在于:Next.js的Middleware默认会等待请求体完全读取后才发送响应,所以即便你提前返回了400错误,服务器还是会接收完整个文件。要实现立即终止上传,需要主动中断请求流,同时补充分块传输场景的处理。

1. 改进Middleware:主动终止请求

修改你的Middleware代码,在返回错误响应前调用request.signal.abort(),直接中断客户端的请求传输:

import { NextResponse } from "next/server";

const MAX_CONTENT_LENGTH = 10000;

export async function middleware(request) {
  if (request.method === "POST" && request.url.endsWith("/meals/share")) {
    const contentLengthStr = request.headers.get("Content-Length");
    
    // 处理Content-Length缺失的情况(比如分块上传)
    if (!contentLengthStr) {
      return NextResponse.json(
        { message: "必须提供Content-Length头部" },
        { status: 400 }
      );
    }

    const contentLength = parseInt(contentLengthStr, 10);
    if (isNaN(contentLength) || contentLength > MAX_CONTENT_LENGTH) {
      // 立即终止请求流,阻止文件继续上传
      request.signal.abort();
      return NextResponse.json(
        { message: `图片大小不能超过${MAX_CONTENT_LENGTH}字节` },
        { status: 400 }
      );
    }
  }
  return NextResponse.next();
}

2. 补充分块上传场景的处理

部分客户端上传大文件时会使用Transfer-Encoding: chunked编码,这种情况下Content-Length头部不存在,Middleware无法提前判断大小。这时候需要在Server Action中实时读取文件流并检查大小:

"use server";

import { writeFile } from "fs/promises";
import { join } from "path";

const MAX_FILE_SIZE = 10000;

export async function shareMeal(formData) {
  const imageFile = formData.get("image");
  if (!imageFile) throw new Error("未上传图片");

  let totalBytes = 0;
  const fileChunks = [];
  const fileStream = imageFile.stream();

  // 逐块读取文件并检查大小
  for await (const chunk of fileStream) {
    totalBytes += chunk.length;
    if (totalBytes > MAX_FILE_SIZE) {
      throw new Error(`图片大小不能超过${MAX_FILE_SIZE}字节`);
    }
    fileChunks.push(chunk);
  }

  // 拼接文件并保存
  const fileBuffer = Buffer.concat(fileChunks);
  const uploadPath = join(process.cwd(), "public", "uploads", imageFile.name);
  await writeFile(uploadPath, fileBuffer);

  // 后续业务逻辑
  // ...
}

原理说明

  • 调用request.signal.abort()会触发请求的中断信号,客户端收到后会立即停止文件传输,避免服务器浪费带宽和存储资源。
  • 分块上传场景下,通过流式读取文件,每读取一块就累加大小,超过限制时直接抛出错误,终止后续读取操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 16:11:09