文件上传,看似就是个「点点按钮 → 传上去」的小功能,但放在企业级应用里,就像让一群人同时走一座独木桥:
- 桥要够宽(支持并发大文件)。
- 桥要够稳(不会掉数据)。
- 桥要够聪明(支持断点续传和安全校验)。
今天我们从底层原理讲起,再结合 Next.js 13/14 的 App Router,做一个可落地的企业级上传方案。
🧩 一、文件上传的底层原理
其实,文件上传就是 客户端通过 HTTP 协议,把二进制字节流丢到服务端 。
但不同场景下的「套路」有点区别:
-
传统 Form 表单
multipart/form-data
格式。- 每个文件被切成一段段 Part,加上边界分隔符上传。
- 适合小文件。
-
AJAX + Fetch
- 利用
FormData
对象。 - 可以更灵活地控制请求头、并发和进度条。
- 利用
-
分块上传(Chunk Upload)
- 大文件(1GB+)必备。
- 把文件切成 N 个小块,逐块上传,最后服务端合并。
- 可以支持「断点续传」。
-
直传云存储(Presigned URL)
- 文件不走业务服务器,而是客户端直传 S3、OSS、COS 等云存储。
- 降低服务端带宽压力,常见于企业级架构。
一句话总结:
小文件 → FormData 就行;大文件 → 分块上传;企业级 → 云直传 + 服务端签名。
⚡ 二、Next.js 中的文件上传
1. 基础版:简单上传
用 API Route 接收文件:
javascript
// app/api/upload/route.js
import { NextResponse } from 'next/server';
import { writeFile } from 'fs/promises';
import path from 'path';
export async function POST(req) {
const formData = await req.formData();
const file = formData.get('file');
// 读取文件内容
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);
// 保存到本地 (仅测试用,生产环境请用云存储)
const filePath = path.join(process.cwd(), 'uploads', file.name);
await writeFile(filePath, buffer);
return NextResponse.json({ message: '上传成功', path: filePath });
}
客户端调用:
javascript
async function uploadFile(file) {
const formData = new FormData();
formData.append("file", file);
const res = await fetch("/api/upload", {
method: "POST",
body: formData,
});
return res.json();
}
⚠️ 缺点:
- 文件都会进 Node.js 内存,1GB 文件可能直接撑爆进程。
- 不适合高并发。
2. 企业级升级:云直传 + 签名
流程:
- 客户端请求服务端要一个「临时上传凭证」。
- 服务端调用云存储(S3、OSS)的 SDK,生成签名 URL。
- 客户端直接把文件 PUT 到云存储。
- 上传完成后,客户端再通知服务端「嘿,文件上传完了」。
示例:生成 S3 上传 URL ⬇️
javascript
// app/api/sign-s3/route.js
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ region: "us-east-1" });
export async function GET() {
const command = new PutObjectCommand({
Bucket: "my-bucket",
Key: `uploads/${Date.now()}.jpg`,
});
const url = await getSignedUrl(s3, command, { expiresIn: 3600 });
return Response.json({ url });
}
客户端上传:
javascript
async function uploadToS3(file) {
const { url } = await fetch("/api/sign-s3").then(r => r.json());
await fetch(url, {
method: "PUT",
body: file,
});
console.log("✅ 上传成功:", url);
}
优势:
- Node.js 不再搬砖(不处理大文件)。
- 云存储具备高可用、断点续传、加密校验等能力。
- 更适合企业生产环境。
3. 分块上传(大文件场景)
核心思想:
- 文件切片:把 2GB 文件切成 5MB/片段。
- 每个切片并发上传。
- 服务端或云存储负责合并。
客户端切片示例:
ini
function sliceFile(file, chunkSize = 5 * 1024 * 1024) {
const chunks = [];
let start = 0;
while (start < file.size) {
let end = Math.min(start + chunkSize, file.size);
chunks.push(file.slice(start, end));
start = end;
}
return chunks;
}
上传的时候加上 进度条 UI,就像看进度条从 0 ➝ 100%,特别适合「企业级大客户」的心理安慰 😆。
🛡️ 三、安全与稳定性
企业级方案,安全性是大头。
- 认证:所有签名接口都必须带用户权限校验。
- 限流:避免单个用户上传 1000 个 10GB 文件,拖垮服务。
- 病毒扫描:某些企业要求对文件做安全扫描(ClamAV 之类)。
- 加密:敏感文件启用服务端加密或 KMS。
一句话:不怕慢,就怕翻车。
🎯 总结
Next.js 文件上传有三重境界:
- 入门级:FormData + API Route → 适合小项目。
- 进阶版:直传云存储 + 签名 URL → 企业标配。
- 大师级:分块上传 + 并发控制 + 审计安全 → 大厂血统。
就像打游戏:
- 普通剑 → 新手村。
- 附魔剑 → 小副本。
- 传说级武器(云直传 + 分块 + 安全) → RAID 团战。
📦 小提示 :如果团队追求极致性能,可以考虑 WebSocket 上传进度回调 ,甚至接入 CDN 边缘计算,把上传体验拉满。