文件存储与 OSS
问题
服务端如何处理文件上传和存储?S3/OSS 的使用方式和最佳实践是什么?
面试速答版
服务端文件存储有哪些方案?为什么生产必上对象存储?
- 本地磁盘:开发用,生产坚决不行(多实例不共享、扩容麻烦、可靠性差)。
- 云对象存储:生产标配,AWS
S3、阿里云OSS、腾讯云COS、CloudflareR2(无出站流量费)、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 | 小文件 | 简单,不推荐大文件 |
云存储服务对比
| 服务 | 提供商 | 特点 |
|---|---|---|
| S3 | AWS | 行业标准,生态最完善 |
| OSS | 阿里云 | 国内首选 |
| COS | 腾讯云 | 国内次选 |
| R2 | Cloudflare | 无出站流量费用 |
| Supabase Storage | Supabase | 开源,与数据库集成 |
上传方式
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 },
});
签名直传优势
- 减轻服务器压力:文件不经过服务器,直接传到 OSS
- 上传更快:少了一次中转
- 节省带宽:服务器不消耗带宽
常见面试问题
Q1: 服务端中转上传和签名直传怎么选?
答案:
- 签名直传(推荐):大文件、高频上传、带宽敏感场景
- 服务端中转:需要对文件做预处理(压缩、水印)、安全要求高
Q2: 如何限制上传文件的大小和类型?
答案:
三层校验:
- 前端:
input accept+ File API 检查(易绕过,仅做体验优化) - 服务端:Content-Type + 文件头(magic number)检测
- OSS 策略:Bucket Policy 限制
Q3: 文件 URL 安全怎么处理?
答案:
- 公开文件:CDN 直接访问
- 私有文件:预签名 URL(带过期时间)
- 敏感文件:Referer 限制 + 签名 + 短期有效
Q4: 如何实现图片上传后自动处理?
答案:
- 上传到 OSS 后触发函数计算(如 Lambda)自动处理
- 使用 OSS 的图片处理参数:
?x-oss-process=image/resize,w_200 - CDN 图片处理服务(如 Cloudflare Images)
相关链接
- 大文件上传和下载 - 分片上传
- 设计图片处理 CDN 服务 - CDN 图片处理
- 设计大文件上传系统 - 文件上传系统设计