30 秒自检:确认是不是这个问题

错误堆栈出现:

  • Body exceeded 1mb limit
  • Request Entity Too Large
  • Payload too large
  • HTTP 413

且发生在 Server Action 调用时。

最短处理路径

Step 1:判断文件大小

大小推荐方案
< 1MBServer Action 默认即可
1-4MB调 bodySizeLimit
4-50MBRoute Handler + streaming
> 50MBPresigned URL

Step 2:调 bodySizeLimit(小文件场景)

next.config.js

module.exports = {
  experimental: {
    serverActions: {
      bodySizeLimit: '4mb',
    },
  },
};

注意:Vercel 平台层 4.5MB 限制,设到 100mb 也没用。

Step 3:用 Presigned URL(大文件场景)

最佳方案。步骤:

// Server Action: 只返回 URL
'use server';
export async function getUploadUrl(filename: string) {
  const presignedUrl = await s3Client.getSignedUrl('putObject', {
    Bucket: 'my-bucket',
    Key: filename,
    Expires: 600,
  });
  return { url: presignedUrl, key: filename };
}
// 客户端
'use client';
const onUpload = async (file: File) => {
  const { url, key } = await getUploadUrl(file.name);
  await fetch(url, {
    method: 'PUT',
    body: file,
    headers: { 'Content-Type': file.type },
  });
  await registerUpload({ key });
};

绝大多数 S3 兼容存储(S3 / R2 / Supabase Storage / Spaces)都支持 Presigned URL。

Step 4:Route Handler 流式处理(中等文件)

// app/api/upload/route.ts
export async function POST(req: Request) {
  const reader = req.body!.getReader();
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    // 流式写入存储
    await writeChunkToStorage(value);
  }
  return Response.json({ ok: true });
}

Request.body.getReader() 让你逐 chunk 处理,不会一次性收完。

Step 5:分块上传

如果文件超过 50MB,且必须经过 Server,用 multipart upload:

  • Client 把文件切成 5MB chunk
  • 每 chunk 用 Presigned URL 单独上传
  • 上传完后调 Server Action 合并

S3 的 multipart upload API 内置这套机制。

原因分析

Next.js Server Action 设计上是「同步函数调用」隐喻,处理 form 数据 + 小响应。大文件传输不是 Server Action 的设计目标。Vercel 平台层 4.5MB 限制是 Lambda 100MB 限制减去 base64 编码膨胀(base64 比二进制大 33%)。

怎么判断是 Next.js 限制还是 Vercel 平台限制?

现象:本地 1GB 文件能上传,Vercel 4.5MB 就 413

Vercel Function payload 限制。本地 Next.js dev server 没有这个限制,所以 local 测试无问题。必须用 Presigned URL。

现象:bodySizeLimit 设了 10mb 还是 413

Vercel 平台层先卡 4.5MB,Next.js 设置无效。要么换 Enterprise(100MB),要么改方案。

现象:Cloudflare Workers 限制更严

Cloudflare Workers Free 单次请求 1MB,Pro 100MB。Next.js + Cloudflare 部署的文件上传必须 Presigned URL。

专业上传服务

考虑用专业上传 SDK:

这些服务把 Presigned URL + 分块 + 进度 + retry 打包,月 $0-50 起步。

跨地区访问 S3 / R2 Dashboard

排查上传问题时要在 S3 / R2 Dashboard 看实际上传情况、CORS 配置。国内访问 AWS Console、Cloudflare R2 Dashboard 偶尔加载缓慢,一条海外服务跑 GitHub Actions / Cloudflare 的稳定线路能让 Dashboard 顺畅、排查更快。

相关报错