从选图开始

用户上传图片、录音、文件,是小程序里最高频的媒体操作之一。第一步是用 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 再复查。

大小与格式治理

上传前先立规矩:图片多大以内、视频多长以内、什么格式允许,都写清楚。超限的直接拦下,告诉用户原因,而不是等传完才发现失败。

上传的流量和存储都是成本,前端多压一压,后端就少烧一点钱。

服务端也要再校验一遍文件类型和大小,别信前端传过来的任何值。文件安全扫描能接就接,用户上传的内容直接进存储,风险不小。

文件命名也别直接拿用户文件名存,容易撞名还可能有特殊字符。服务端统一重命名,按业务和日期分目录放,后续清理和审计都好做。

用户删掉的内容也要管:数据库里引用删了,存储里的文件容易变孤儿。定期跑清理任务,把不再被引用的文件删掉,能省下不少存储费用。

还有一点容易被忽略:上传接口的带宽和并发也要限流。某个活动期间用户集中上传,不限制的话后端容易被打爆,提前做好配额和排队,比事后扩容更省心。