用简单的代码,将小程序文件直传到腾讯云COS实践

简介

本文介绍如何不依赖 SDK,用简单的代码,在小程序直传文件到腾讯云COS的存储桶。

注意:

本文档内容基于 XML 版本的 API。

前期条件

登录对象存储控制台,创建存储桶,设置 BucketName(存储桶名称) 和 Region(地域名称),详情请参见创建存储桶文档。

登录访问管理控制台,进入 API 密钥管理页面,获取您的项目 SecretId 和 SecretKey。

注意:

目前腾讯云有COS特惠活动,新人1元起

实践步骤

1. 配置小程序域名白名单

小程序里请求腾讯云COS需要登录到微信公众平台,在“开发”->“开发设置”中,配置域名白名单。SDK 用到了两个接口:wx.uploadFile 和 wx.request。

cos.postObject 使用 wx.uploadFile 发送请求。

其他方法使用 wx.request 发送请求。

两者都需要在对应白名单里,配置 COS 域名。白名单里配置的域名格式有两种:

如果只用到一个存储桶,可以配置 Bucket 域名作为白名单域名,例如examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com。

如果用到多个存储桶,可以选择后缀式请求 COS,把 bucket 放在 pathname 里请求,这种方式需要配置地域域名作为白名单,例如cos.ap-guangzhou.myqcloud.com。具体可参考下文代码中的注释。

2. 获取临时密钥和计算签名

出于安全考虑,签名使用临时密钥,服务端搭建临时密钥服务,请参见PHP 示例Nodejs 示例

如有其他语言或自行实现可参考以下流程:

(1)向服务端获取临时密钥,服务端首先使用固定密钥 SecretId、SecretKey 向 STS 服务获取临时密钥,得到临时密钥 tmpSecretId、tmpSecretKey、sessionToken,详情请参见临时密钥生成及使用指引cos-sts-sdk

注意:

根据使用的请求是put还是post,STS 的 policy action 要加上允许 "name/cos:PutObject"或"name/cos:PostObject"。

(2)前端通过 tmpSecretId、tmpSecretKey,以及 method、pathname 计算签名,可参考下文使用cos-auth.js来计算签名,如果业务需要也可以放在后端计算签名。

(3)将计算得到的签名和 sessionToken,分别放到

post请求的formData 的 Signature 和 x-cos-security-token 字段里,向 COS API 发出上传请求。

put请求的headers 的 Signature 和 x-cos-security-token 字段里,向 COS API 发出上传请求。

注意:

正式部署时服务端请加一层您的网站本身的权限检验。

3. 后缀式请求

COS API 一般的请求格式都类似POST http://examplebucket-1250000000.cos.ap-beijing.myqcloud.com/,请求的域名是存储桶域名。这样如果在小程序里用到多个存储桶,则需要配置这个存储桶域名作为白名单域名。解决方法如下:

COS 提供了后缀式请求格式POST http://cos.ap-beijing.myqcloud.com/examplebucket-1250000000/,请求的域名是地域域名,存储桶名称放在请求的路径里。在小程序里用到同一个地域多个存储桶,只需要配置一个域名cos.ap-beijing.myqcloud.com作为白名单域名。

后缀式请求格式需要注意,签名时使用的路径要用以存储桶名称作为前缀的路径,例如/examplebucket-1250000000/。

4. 直传示例代码

以下代码同时举例了PUT Object 接口(推荐使用)和POST Object 接口,操作指引如下:



varCosAuth=require('./cos-auth');// 这里引用了 cos-auth.js,下载地址为 https://unpkg.com/cos-js-sdk-v5/demo/common/cos-auth.min.js varBucket='examplebucket-1250000000';varRegion='ap-shanghai';varForcePathStyle=false;// 是否使用后缀式,涉及签名计算和域名白名单配置,后缀式说明看上文varuploadFile =function() {// 请求用到的参数varprefix ='https://'+Bucket+'.cos.'+Region+'.myqcloud.com/';if(ForcePathStyle) {// 后缀式请求在签名时域名使用地域域名,而不是存储桶域名,具体说明见本文上述“3.后缀式请求”prefix ='https://cos.'+Region+'.myqcloud.com/'+Bucket+'/';    }// 对更多字符编码的 url encode 格式varcamSafeUrlEncode =function(str) {returnencodeURIComponent(str)            .replace(/!/g,'%21')            .replace(/'/g,'%27')            .replace(/\(/g,'%28')            .replace(/\)/g,'%29')            .replace(/\*/g,'%2A');    };// 获取临时密钥varstsCache;vargetCredentials =function(callback) {if(stsCache &&Date.now() /1000+30< stsCache.expiredTime) {callback(data.credentials);return;        }        wx.request({method:'GET',url:'https://example.com/sts.php',// 服务端签名,参考上文说的获取临时密钥dataType:'json',success:function(result) {vardata = result.data;varcredentials = data.credentials;if(credentials) {                    stsCache = data                }else{                    wx.showModal({title:'临时密钥获取失败',content:JSON.stringify(data),showCancel:false});                }callback(stsCache && stsCache.credentials);            },error:function(err) {                wx.showModal({title:'临时密钥获取失败',content:JSON.stringify(err),showCancel:false});            }        });    };// 计算签名vargetAuthorization =function(options, callback) {getCredentials(function(credentials) {callback({XCosSecurityToken: credentials.sessionToken,Authorization:CosAuth({SecretId: credentials.tmpSecretId,SecretKey: credentials.tmpSecretKey,Method: options.Method,Pathname: options.Pathname,                })            });        });    };// post上传文件varpostFile =function(filePath) {varKey= filePath.substr(filePath.lastIndexOf('/') +1);// 这里指定上传的文件名varsignPathname ='/';// PostObject 接口 Key 是放在 Body 传输,所以请求路径和签名路径是 /if(ForcePathStyle) {// 后缀式请求在签名时用的路径,要包含存储桶名称,具体说明见本文上述“3.后缀式请求”signPathname ='/'+Bucket+'/';          }getAuthorization({Method:'POST',Pathname: signPathname},function(AuthData) {varrequestTask = wx.uploadFile({url: prefix,name:'file',filePath: filePath,formData: {'key':Key,'success_action_status':200,'Signature':AuthData.Authorization,'x-cos-security-token':AuthData.XCosSecurityToken,'Content-Type':'',                },success:function(res) {varurl = prefix +camSafeUrlEncode(Key).replace(/%2F/g,'/');console.log(res.statusCode);console.log(url);if(/^2\d\d$/.test(''+ res.statusCode)) {                        wx.showModal({title:'上传成功',content: url,showCancel:false});                    }else{                        wx.showModal({title:'上传失败',content:JSON.stringify(res),showCancel:false});                    }                },fail:function(res) {                    wx.showModal({title:'上传失败',content:JSON.stringify(res),showCancel:false});                }            });            requestTask.onProgressUpdate(function(res) {console.log('进度:', res);            });        });    };// put上传文件varputFile =function(filePath) {varKey= filePath.substr(filePath.lastIndexOf('/') +1);// 这里指定上传的文件名varsignPathname ='/'+Key;// PutObject 接口 Key 是放在 url 传输,所以请求路径和签名路径是 /Keyif(ForcePathStyle) {// 后缀式请求在签名时用的路径,要包含存储桶名称,具体说明见本文上述“3.后缀式请求”signPathname ='/'+Bucket+'/'+Key;          }getAuthorization({Method:'PUT',Pathname: signPathname},function(AuthData) {// put请求需要从文件临时路径读取出文件内容varwxfs = wx.getFileSystemManager();          wxfs.readFile({filePath: filePath,success:function(fileRes) {varrequestTask = wx.request({url: prefix + signPathname.substr(signPathname.lastIndexOf('/') +1),method:'PUT',header: {'Authorization':AuthData.Authorization,'x-cos-security-token':AuthData.XCosSecurityToken,                },data: fileRes.data,success:functionsuccess(res) {varurl = prefix +camSafeUrlEncode(Key).replace(/%2F/g,'/');if(res.statusCode===200) {                      wx.showModal({title:'上传成功',content: url,showCancel:false});                  }else{                      wx.showModal({title:'上传失败',content:JSON.stringify(res),showCancel:false});                  }console.log(res.statusCode);console.log(url);                },fail:functionfail(res) {                  wx.showModal({title:'上传失败',content:JSON.stringify(res),showCancel:false});                },              });              requestTask.onProgressUpdate(function(res) {console.log('正在进度:', res);              });            },          });        });    };// 选择文件wx.chooseImage({count:1,// 默认9sizeType: ['original'],// 可以指定是原图还是压缩图,这里默认用原图sourceType: ['album','camera'],// 可以指定来源是相册还是相机,默认二者都有success:function(res) {putFile(res.tempFiles[0].path);// put请求上传,推荐使用// postFile(res.tempFiles[0].path); // post请求上传}    })};

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容