小程序最烫手的两类数据是图片和文件:用户头像、商品图、聊天图片、表单附件。存服务器本地?带宽先爆、硬盘再爆、备份哭死。正确姿势是对象存储 + CDN + 直传——这篇讲清楚为什么、怎么做。
一、为什么不能存服务器本地
算一笔账就明白。一个电商小程序,日均上传 500 张商品图,平均 2MB:
| 指标 | 存本地 | 对象存储 |
|---|---|---|
| 日增硬盘 | 1GB | 0(在云端) |
| 月增硬盘 | 30GB | 0 |
| 下载流量 | 全走服务器带宽 | 走 CDN,服务器零参与 |
| 带宽压力 | 5M 带宽 3 人同时下图就满 | 无限并发 |
| 备份 | 要连文件一起备,越备越大 | 存储商多副本,天然高可用 |
| 单点 | 服务器挂了图全丢 | 服务商 SLA |
结论:只要文件量会上涨,就必须对象存储。只有"几十张静态图"的演示项目可以存本地。
二、上传的三种姿势
姿势 A:小程序 → 你的服务器 → 对象存储(中转)
小程序 --上传--> 你的服务器 --转发--> 对象存储优点:简单,服务端能做格式校验、鉴权、压缩。缺点:流量过你的服务器,大文件高峰期带宽和 CPU 双杀。适合小规模。
姿势 B:小程序 → 对象存储直传(推荐)
小程序 --(拿临时密钥)--> 直接上传到对象存储优点:服务器只发一个"临时凭证",上传流量完全不经过你的机器——带宽压力清零,还能利用存储商的 CDN 加速。缺点:要配临时密钥(STS)和跨域(CORS)规则。这是生产环境的标准方案。
姿势 C:云开发存储
用微信云开发的 wx.cloud.uploadFile,最省事,但数据在腾讯云上,且走云开发的计费体系——选型对比见小程序云开发 vs 自建后端。
三、姿势 B 完整实现
1. 小程序端:选择文件 + 上传
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['compressed'],
success: async (res) => {
const tempFilePath = res.tempFiles[0].tempFilePath
// 先从自己服务端拿临时上传凭证
const { data } = await wx.request({
url: 'https://api.yourdomain.com/sts/token',
method: 'POST'
})
// 直传到对象存储(以腾讯云 COS 小程序 SDK 为例)
const cos = new COS({
getAuthorization: (options, callback) => {
callback({
TmpSecretId: data.tmpSecretId,
TmpSecretKey: data.tmpSecretKey,
SecurityToken: data.sessionToken,
StartTime: data.startTime,
ExpiredTime: data.expiredTime
})
}
})
cos.putObject({
Bucket: 'your-bucket-1250000000',
Region: 'ap-guangzhou',
Key: 'avatar/' + Date.now() + '.jpg',
FilePath: tempFilePath
}, (err, result) => {
if (!err) console.log('上传成功', result.Location)
})
}
})2. 服务端:签发临时凭证
临时密钥用云厂商的 STS 服务(Python/Node/PHP 都有 SDK),核心逻辑:
const STS = require('tencentcloud-sdk-nodejs/tencentcloud/services/sts/v20180813')
const sts = new STS({ /* 用主账号密钥初始化,密钥只存服务端 */ })
app.post('/sts/token', async (req, res) => {
// 实际项目要校验用户登录态,按用户限频限配额
const policy = {
version: '2.0',
statement: [{
effect: 'allow',
action: ['cos:PutObject'], // 只给上传权限
resource: ['qcs::cos:ap-guangzhou:uid/1250000000:bucket-1250000000/avatar/*']
}]
}
const result = await sts.AssumeRole({
RoleArn: '你的角色ARN',
RoleSessionName: 'miniapp-upload',
DurationSeconds: 1800, // 短时效,30 分钟
Policy: JSON.stringify(policy)
})
res.json(result.Credentials)
})权限三原则:只给 PutObject(不给读不给删)、只给 avatar/* 前缀(不给整桶)、短时效(30 分钟内)。主账号密钥永远不出服务端——它出现在小程序代码里的那天,就是桶被搬空的那天。
3. 对象存储配置
- CORS 规则:允许小程序域名/上传域名的跨源请求,否则上传被浏览器策略拦
- 防盗链:Referer 白名单,别让别人的站直接引用你的图
- CDN 加速:开存储商的 CDN,全球下载都快,见CDN 加速配置实战
四、图片处理:上传只是开始
对象存储的杀手锏是图片处理 API,在 URL 后面拼参数就能实时处理:
原图: https://cdn.yourdomain.com/avatar/123.jpg
缩略图: https://cdn.yourdomain.com/avatar/123.jpg?imageMogr2/thumbnail/300x
WebP: https://cdn.yourdomain.com/avatar/123.jpg?imageMogr2/format/webp
质量压缩: https://cdn.yourdomain.com/avatar/123.jpg?imageMogr2/quality/80
水印: https://cdn.yourdomain.com/avatar/123.jpg?watermark/2/text/xxx用法:列表页用缩略图、详情页用原图、按需转 WebP——同一张图多场景复用,存储和流量都省。图片优化的完整方法论见网站图片与静态资源优化。
小程序端压缩:wx.compressImage 先压再传,用户流量和上传时间双降:
wx.compressImage({
src: tempFilePath,
quality: 80, // 80 质量肉眼无损
success: (res) => { /* 用 res.tempFilePath 上传 */ }
})五、下载与鉴权
对象存储的读取分两种:
| 模式 | 用途 | 配置 |
|---|---|---|
| 公有读 | 头像、商品图(谁都能看) | 桶/对象公有读 + CDN |
| 私有读 | 用户身份证、合同、订单凭证 | 私有桶 + 服务端签临时 URL |
私有文件的下载链接要服务端按需签发(带时效的预签名 URL),别图省事设成公有读——用户隐私文件公网可遍历是数据泄露的重灾区,处置流程见数据泄露应急响应与合规要点。
六、运维清单
- [ ] 主账号密钥只在服务端环境变量,不进代码库(用 .env + .gitignore,见Git 仓库与代码泄露防范)
- [ ] 上传限频限类型(单用户每分钟 N 张,只许 jpg/png/webp)
- [ ] 单文件大小上限(小程序 uploadFile 本身限 10MB)
- [ ] 生命周期规则:临时文件 7 天自动清理,别让废弃上传堆成山
- [ ] 元数据入库:谁传的、什么时候、Key 是什么——数据库和存储对不上账时,元数据是唯一线索
- [ ] 跨账号容灾:核心文件定期同步到第二个桶/账号,服务商出事故时能恢复
结语
图片存储的黄金法则:上传走直传、下载走 CDN、处理用 URL 参数、权限按对象配。这套方案让服务器彻底从"文件搬运工"里解放出来——机器只跑业务逻辑,带宽和硬盘的压力归零,扩容就是改个桶的事。
需要搭小程序后端,可以看看财富云计算:NAT 型云服务器支持 443 映射,配合对象存储做图片直传,业务服务器 2核2G 就够——图片的事交给云存储,服务器的钱省一半。
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复