uni-app直传腾讯云COS的唯一可行路径是PostObject表单上传,需后端提供bucket、region、key、policy、signature及临时密钥三元组,前端用uni.uploadFile调用且name必须为file、formData字段大小写敏感、url为标准COS域名。

uni-app 把图片上传到腾讯云 COS,核心不是“能不能传”,而是“怎么绕过服务器直传”——否则你只是把文件先发到自己的后端,再由后端转发到 COS,白白消耗带宽、增加延迟、放大单点风险。
直传是唯一合理路径,但必须满足三个前提:前端能拿到临时密钥、签名计算正确、uni.uploadFile 调用参数匹配 PostObject 协议。缺一不可。
为什么不能直接用 cos-wx-sdk-v5 的 putObject?
很多开发者一上来就引入 cos-wx-sdk-v5.min.js,调 cos.putObject,结果在 H5 或 App 端报错:Failed to execute 'put' on 'XMLHttpRequest': Invalid URL 或 Network Error。
原因很实在:putObject 依赖浏览器原生 fetch / XMLHttpRequest 发起 PUT 请求,而 H5 环境下跨域限制严格,且部分安卓 WebView 对 PUT 支持不完整;小程序虽支持,但 uni.uploadFile 本身不封装 PUT,强行用 SDK 内部逻辑会破坏 uni-app 的统一请求生命周期(比如无法监听进度、无法自动重试)。
真正兼容全端(App/H5/微信小程序/支付宝小程序)的方案,是走 PostObject 表单上传,底层只用 uni.uploadFile ——它本质是 POST + multipart/form-data,所有平台都稳。
-
uni.uploadFile的name必须固定为file(COS PostObject 协议硬性要求) -
formData中必须包含key、policy、signature、cos_bucket、cos_region等字段,缺一不可 - 后端返回的
policy和signature必须基于 UTC 时间生成,且policy中的expiration不能超过 7 天(COS 限制)
后端必须返回哪些字段才能让前端直传成功?
前端调用 uni.uploadFile 前,必须先请求后端接口(如 /api/v1/cos/upload-token)获取上传凭证。这个接口返回的 JSON 至少要含以下字段:
-
bucket:存储桶全名,例如ruoyi-vue-1250000000 -
region:地域代码,例如ap-guangzhou -
key:上传后的对象路径,建议带前缀和随机名,如images/20260805/abc123.jpg -
policy:Base64 编码的 JSON 策略字符串(含 expiration、conditions 等) -
signature:对policy使用临时密钥SecretId+SecretKey+sessionToken计算出的 HMAC-SHA1 签名 -
credentials对象里必须有tmpSecretId、tmpSecretKey、sessionToken——这是临时密钥三元组,有效期通常 30 分钟,绝不能暴露长期密钥
注意:policy 中的 conditions 字段必须包含 ["starts-with", "$key", "images/"] 这类限制,否则 COS 会拒绝上传——这是安全底线,不是可选项。
uni.uploadFile 调用时最容易填错的参数
很多人卡在最后一步:明明拿到了 token,uni.uploadFile 却返回 403 Forbidden 或 400 Bad Request。问题往往出在 formData 构造上。
-
url必须是 COS 的 PostObject 地址,格式为https://<bucket>.cos.<region>.myqcloud.com,不能加任何 query 参数,也不能漏掉https:// -
filePath是本地临时路径(如uni.chooseImage返回的tempFilePaths[0]),不是 base64 字符串 -
name必须是"file",写成"myfile"或"img"都会失败 -
formData是一个 plain object,不是 FormData 实例;字段名大小写敏感,key不是Key,policy不是Policy - 如果上传 jpg 图片但服务端策略限制了
content-type,需在formData中显式加上content-type: "image/jpeg",否则 COS 可能校验失败
示例片段:
uni.uploadFile({
url: 'https://ruoyi-vue-1250000000.cos.ap-guangzhou.myqcloud.com',
filePath: tempPath,
name: 'file',
formData: {
key: 'images/20260805/abc123.jpg',
policy: 'eyJleHBpcmF0aW9uIjoiMjAyNi0wOC0wNFQwODowMDowMFoiLCJjb25kaXRpb25zIjpbWyJzdGFydHMtd2l0aCIsIiRrZXkiLCJpbWFnZXMvIl1dfQ==',
signature: 'XKd...+fA==',
cos_bucket: 'ruoyi-vue-1250000000',
cos_region: 'ap-guangzhou',
content-type: 'image/jpeg'
},
success: (res) => {
if (res.statusCode === 204) {
console.log('上传成功')
}
}
})
多图上传时别用并发,改用串行控制
看到“多图上传”就立刻写 Promise.all 是常见误区。COS 的 PostObject 接口对同一 bucket + region 的并发请求有限流(尤其免费层),同时发 5 张图,大概率出现 429 Too Many Requests 或部分成功部分失败。
更稳妥的做法是串行上传,每张图上传完再触发下一张:
- 用
for...of循环,配合await,确保顺序执行 - 每张图单独请求一次
/api/v1/cos/upload-token,避免 token 复用超时(token 有效期短,复用风险高) - 上传失败时记录错误并跳过,不要中断整个流程;失败后可尝试重试 1 次,但不要无限重试
真正麻烦的从来不是代码量,而是 token 时效性、policy 条件一致性、以及不同端对 uni.uploadFile 的细微行为差异——比如 H5 端某些版本会忽略 formData 中的 content-type,而小程序严格校验。这些细节不踩一遍坑,很难真正跑通。


















