跳到主要内容

文件存储与 OSS

问题

服务端如何处理文件上传和存储?S3/OSS 的使用方式和最佳实践是什么?

面试速答版

服务端文件存储有哪些方案?为什么生产必上对象存储?

  • 本地磁盘:开发用,生产坚决不行(多实例不共享、扩容麻烦、可靠性差)。
  • 云对象存储:生产标配,AWS S3、阿里云 OSS、腾讯云 COS、Cloudflare R2(无出站流量费)、Supabase Storage(开源)。
  • 数据库 BLOB:只适合小文件(如头像 < 100KB),大文件会拖垮数据库。
  • 配套 CDN:对象存储一定要套 CDN 加速访问,URL 形如 https://cdn.example.com/uploads/xxx.jpg

服务端中转 vs 签名直传怎么选? 两种上传方式各有适用场景:

  • 签名直传(推荐):后端用 getSignedUrl 签发预签名 URL(5 分钟有效),前端拿到后直接 PUT 到 OSS——文件不过服务器,节省带宽、上传更快。
  • 服务端中转:需要预处理(压缩、水印、敏感内容审核)或安全要求高时用,文件经过 Node.js 再转 OSS。
  • 限制大小/类型:三层校验——前端 accept 改善体验、服务端校验 Content-Type + magic number、OSS Bucket Policy 兜底。
  • 私有文件:用预签名 URL 限期访问;公开文件直接 CDN;敏感文件加 Referer 限制 + 短期签名。
  • 图片处理:OSS 自带处理参数(?x-oss-process=image/resize,w_200)或 Cloudflare Images,不用自己写。

答案

文件存储方案

方案适用场景优缺点
本地磁盘开发环境简单,不适合生产
云对象存储生产环境可靠、CDN 加速、按量计费
数据库 BLOB小文件简单,不推荐大文件

云存储服务对比

服务提供商特点
S3AWS行业标准,生态最完善
OSS阿里云国内首选
COS腾讯云国内次选
R2Cloudflare无出站流量费用
Supabase StorageSupabase开源,与数据库集成

上传方式

1. 服务端中转上传

server-upload.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({ region: 'us-east-1' });

app.post('/api/upload', upload.single('file'), async (req, res) => {
const file = req.file!;
const key = `uploads/${Date.now()}-${file.originalname}`;

await s3.send(new PutObjectCommand({
Bucket: 'my-bucket',
Key: key,
Body: file.buffer,
ContentType: file.mimetype,
}));

res.json({ url: `https://cdn.example.com/${key}` });
});

2. 签名直传(推荐)

presigned-upload.ts
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

// 后端:生成预签名 URL
app.post('/api/upload/presign', async (req, res) => {
const { filename, contentType } = req.body;
const key = `uploads/${Date.now()}-${filename}`;

const url = await getSignedUrl(s3, new PutObjectCommand({
Bucket: 'my-bucket',
Key: key,
ContentType: contentType,
}), { expiresIn: 300 }); // 5 分钟有效

res.json({ uploadUrl: url, fileUrl: `https://cdn.example.com/${key}` });
});

// 前端:直接上传到 OSS
const { uploadUrl, fileUrl } = await api.getPresignedUrl(file.name, file.type);
await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type },
});
签名直传优势
  1. 减轻服务器压力:文件不经过服务器,直接传到 OSS
  2. 上传更快:少了一次中转
  3. 节省带宽:服务器不消耗带宽

常见面试问题

Q1: 服务端中转上传和签名直传怎么选?

答案

  • 签名直传(推荐):大文件、高频上传、带宽敏感场景
  • 服务端中转:需要对文件做预处理(压缩、水印)、安全要求高

Q2: 如何限制上传文件的大小和类型?

答案

三层校验:

  1. 前端input accept + File API 检查(易绕过,仅做体验优化)
  2. 服务端:Content-Type + 文件头(magic number)检测
  3. OSS 策略:Bucket Policy 限制

Q3: 文件 URL 安全怎么处理?

答案

  • 公开文件:CDN 直接访问
  • 私有文件:预签名 URL(带过期时间)
  • 敏感文件:Referer 限制 + 签名 + 短期有效

Q4: 如何实现图片上传后自动处理?

答案

  1. 上传到 OSS 后触发函数计算(如 Lambda)自动处理
  2. 使用 OSS 的图片处理参数:?x-oss-process=image/resize,w_200
  3. CDN 图片处理服务(如 Cloudflare Images)

相关链接