从选图开始
用户上传图片、录音、文件,是小程序里最高频的媒体操作之一。第一步是用 wx.chooseMedia 让用户从相册选图或者现场拍一张,它返回的是一批临时文件路径,还不能直接当永久地址用。
选图时可以直接让用户限制数量,比如头像选一张、相册最多九张。sizeType 可以指定原图还是压缩图,很多场景选压缩图就够了,能省掉一大截上传流量。
拿到临时路径之后别急着传,先看一眼文件大小。一张手机原图动辄几兆,直接传既慢又费流量,压缩是上传链路里省不掉的一步。
- 选图chooseMedia 取临时路径。
- 压缩compressImage 降体积。
- 上传uploadFile 带进度。
- 直传云存储免自建服务。
压缩再上传
wx.compressImage 可以把大图压到指定质量,返回值还是临时路径。压缩完再比较一次大小,太小的说明已经是压缩图,就别再压了。
wx.compressImage({
src: tempFilePath,
quality: 70,
success: function (res) {
var small = res.tempFilePath;
upload(small);
}
});
质量调到七十左右,肉眼基本看不出差别,体积能小一大半。视频压缩也一样,先压再传,别把几十分钟的原始视频直接扔到上传队列里。
注意临时文件是有生命周期的,页面关掉可能就失效了。要么在拿到后立刻处理,要么先复制到用户目录保存起来。
上传接口与进度
小文件用 wx.uploadFile 直接传,url 指向自己的服务端接口,filePath 填压缩后的路径,name 是服务端接收的字段名。传完服务端返回一个文件地址,存进数据库。
wx.uploadFile({
url: 'https://api.example.com/upload',
filePath: small,
name: 'file',
success: function (res) {
var fileUrl = JSON.parse(res.data).url;
}
});
大文件建议换成分片上传:前端把文件切成一块一块,逐块传,服务端按序号拼起来。断点续传、失败重传都好做,用户弱网也不至于从头再来。
上传进度要用 onProgressUpdate 监听,给用户一个看得见的进度条,传输失败要给重试按钮,别让用户对着一个永远转圈的菊花干等。
多文件上传建议做成队列:一个接一个传,别十个一起并发。队列里记录每个文件的状态,成功、失败、重试一目了然,用户也好知道到底哪个文件没传上去。
直传云存储
用云开发的话,上传可以不走自己的服务器,直接传云存储。wx.cloud.uploadFile 把本地文件传到云端,返回 fileID,拿到就能在数据库里引用。
| 方式 | 适用 | 成本 |
|---|---|---|
| 自建上传接口 | 已有服务端 | 要维护接口 |
| 云存储直传 | 云开发项目 | 按量计费 |
| 分片直传 | 大文件 | 实现稍复杂 |
直传的优点是快:文件不经过自己的服务器中转,客户端直接怼到对象存储。缺点是没法在途做业务校验,所以校验逻辑要么放前端,要么等服务端收到 fileID 再复查。
大小与格式治理
上传前先立规矩:图片多大以内、视频多长以内、什么格式允许,都写清楚。超限的直接拦下,告诉用户原因,而不是等传完才发现失败。
上传的流量和存储都是成本,前端多压一压,后端就少烧一点钱。
服务端也要再校验一遍文件类型和大小,别信前端传过来的任何值。文件安全扫描能接就接,用户上传的内容直接进存储,风险不小。
文件命名也别直接拿用户文件名存,容易撞名还可能有特殊字符。服务端统一重命名,按业务和日期分目录放,后续清理和审计都好做。
用户删掉的内容也要管:数据库里引用删了,存储里的文件容易变孤儿。定期跑清理任务,把不再被引用的文件删掉,能省下不少存储费用。
还有一点容易被忽略:上传接口的带宽和并发也要限流。某个活动期间用户集中上传,不限制的话后端容易被打爆,提前做好配额和排队,比事后扩容更省心。
常见上传坑
| 坑 | 后果 | 应对 |
|---|---|---|
| 原图直接传 | 慢且费流量 | 先压缩再上传 |
| 临时文件过期 | 上传报错 | 先复制再传 |
| 无进度提示 | 用户干等 | 监听上传进度 |
| 服务端不校验 | 脏文件入库 | 双重校验加扫描 |
落地清单
- 选图时控制数量和尺寸。
- 上传前统一压缩。
- 大文件走分片加断点续传。
- 进度、失败、重试都给用户反馈。
- 服务端校验类型、大小并做安全扫描。
上传链路顺不顺,直接写在用户的第一印象里。