如何在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
相关产品推荐
相关产品推荐

