小程序直传OBS
背景信息
微信小程序作为当下流行的移动应用,具有广泛的应用场景。如何通过微信小程序上传文件至对象存储服务OBS成为了一个热点问题,本文将通过一个示例程序进行演示。
注意事项
- 客户端计算签名时依赖引用“crypto-js”及“js-base64”两个开源组件,因此需要在微信小程序项目中设置使用NPM模块。
- 在微信小程序中进行编译时,如果在引入“crypto-js”包时出现“Maximum call stack size exceed”报错,请升级微信小程序开发客户端至最新版本。
- 上传过程中返回405时,请检查指定的endpoint是否为对应上传桶的桶域名。
操作步骤
- 设置桶的跨域访问权限。
微信小程序基于BrowerJS进行开发,受同源安全策略的要求,不同域间的网站脚本和内容如需交互,需要配置跨域资源共享(CORS)规范。华为云对象存储服务OBS支持CORS规范,允许跨域访问OBS中的资源,具体配置步骤请参见配置跨域资源共享。
CORS规则配置项建议:
表1 CORS规则 参数
说明
配置建议
允许的来源
必选参数,指定允许的跨域请求的来源,即允许来自该域名下的请求访问该桶。
允许多条匹配规则,以回车换行为间隔。每个匹配规则允许使用最多一个“*”通配符。例如:
http://rds.example.com https://*.vbs.example.com
*
允许的方法
必选参数,指定允许的跨域请求方法,即桶和对象的几种操作类型。包括:Get、Post、Put、Delete、Head。
全选
允许的头域
可选参数,指定允许的跨域请求的头域。只有匹配上允许的头域中的配置,才被视为是合法的CORS请求。
允许的头域可设置多个,多个头域之间换行隔开,每行最多可填写一个*符号,不支持&、:、<、空格以及中文字符。
*
补充头域
可选参数,指CORS响应中带的补充头域,给客户端提供额外的信息。
补充头域可设置多个,多个头域之间换行隔开,不支持*、&、:、<、空格以及中文字符。
- ETag
- x-obs-request-id
- x-obs-api
- Content-Type
- Content-Length
- Cache-Control
- Content-Disposition
- Content-Encoding
- Content-Language
- Expires
- x-obs-id-2
- x-reserved-indicator
- x-obs-version-id
- x-obs-copy-source-version-id
- x-obs-storage-class
- x-obs-delete-marker
- x-obs-expiration
- x-obs-website-redirect-location
- x-obs-restore
- x-obs-version
- x-obs-object-type
- x-obs-next-append-position
缓存时间
必选参数,请求来源的客户端可以缓存的CORS响应时间,以秒为单位,默认为100秒。
根据实际业务设置。
- 配置小程序上传域名白名单。
微信小程序利用白名单机制管理跨域访问,想要实现数据上传,需要在微信小程序平台域名白名单中配置桶的访问域名。
- 计算POST上传签名。
POST上传前需要根据上传时自定义使用的policy字段计算相关签名信息,签名计算规则请参见基于浏览器上传的表单中携带签名,计算签名相关源代码如下:
对policy进行base64编码(GetPolicy.js):
const Base64 = require('js-base64'); function getPolicyEncode(policy) { // 传入表单上传的policy字段,对policy进行Base64编码 const encodedPolicy = Base64.encode(JSON.stringify(policy)); return encodedPolicy; } module.exports = getPolicyEncode;
计算签名的源代码(GetSignature.js):
const Crypto = require('crypto-js'); const Base64 = require('js-base64'); function getSignature(policyEncoded, SecretKey){ // 利用SK对Base64编码后的policy结果进行HMAC-SHA1签名计算 const bytes = Crypto.HmacSHA1(policyEncoded, SecretKey); // 对计算结果进行Base64编码,得到最终的签名信息 const signature = Crypto.enc.Base64.stringify(bytes); return signature; } module.exports = getSignature;
- 使用小程序直传数据至对象存储桶中。
基于3中得到的编码后的policy字段及signature字段,可以调用小程序中的上传接口,选择本地文件并上传。具体代码示例如下:
配置AK、SK、访问域名等信息的配置文件(Configuration.js):
- 使用永久访问秘钥(AK/SK)
// 指定OBS服务相关信息:AK,SK,EndPoint var Configuration = { // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyId和SecretKey。 // 前端本身没有process对象,可以使用webpack类打包工具定义环境变量,就可以在代码中运行了。 // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html AccessKeyId: process.env.AccessKeyID, SecretKey: process.env.SecretAccessKey, EndPoint: 'https://your-test-bucket.obs.myhuaweicloud.com', //完整的桶访问域名 }; module.exports = Configuration;
- 使用临时访问秘钥(AK/SK/securitytoken)
获取临时AK/SK和securitytoken的方法,请参见获取临时AK/SK和securitytoken。
// 指定OBS服务相关信息:AK,SK,SecurityToken,EndPoint var Configuration = { // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyId和SecretKey。 // 前端本身没有process对象,可以使用webpack类打包工具定义环境变量,就可以在代码中运行了。 // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html AccessKeyId: process.env.AccessKeyID, SecretKey: process.env.SecretAccessKey, SecurityToken: process.env.SecurityToken, //securityToken EndPoint: 'https://your-test-bucket.obs.myhuaweicloud.com', //完整的桶访问域名 }; module.exports = Configuration;
配置文件中传入的EndPoint应该为完整的桶访问域名,例如:https://bucketName.obs.myhuaweicloud.com,其中bucketName即小程序上传的目标桶名。
// 引入配置文件 const config = require('./Configuration.js'); // 引入policy编码计算方法 const getPolicyEncode = require('./getPolicy.js'); // 引入签名计算方法 const getSignature = require('./GetSignature.js'); const OBSupload = function (filePath){ if(!filePath){ wx.showToast({ title: 'Invalid filePath', icon: 'Please re-select path', }); } else{ const fileName = 'testMiniprogram.jpg'; // 指定上传到OBS桶中的对象名 const OBSPolicy = { // 设定policy内容,policy规则定义可参考步骤3中的超链接签名计算规则文档 "expiration": "2021-12-31T12:00:00.000Z", "conditions": [ { "bucket": "your-test-bucket" }, // 桶名要和配置文件中endpoint中的桶名保持一致 // { "x-obs-security-token": config.SecurityToken } // 如果是临时访问秘钥鉴权,必须设置该值 { 'key': fileName } ] } const policyEncoded = getPolicyEncode(OBSPolicy); // 计算base64编码后的policy const signature = getSignature(policyEncoded, config.SecretKey); // 计算signature wx.uploadFile({ url: config.EndPoint, filePath: filePath, name: 'file', header: { 'content-type': 'multipart/form-data; boundary=-9431149156168', }, formData: { // 从配置文件中获取到的AK信息、计算得到的编码后policy及signature信息 'AccessKeyID': config.AccessKeyId, 'policy': policyEncoded, 'signature': signature, 'key': fileName, // "x-obs-security-token": config.SecurityToken, // 如果是临时访问秘钥鉴权,必须设置该值 }, success: function(res){ console.log(res.statusCode); //打印响应状态码 if(res.statusCode=='204'){ console.log('Uploaded successfully', res) wx.showToast({ title: 'Uploaded successfully', icon: 'Success' }); } else{ console.log('Uploaded failed', res) wx.showToast({ title: 'Uploaded failed', icon: 'Fail' }); } }, fail: function(e){ console.log(e); } }) } } module.exports = OBSupload;
- 使用永久访问秘钥(AK/SK)
相关操作
上传完成后,要获取对应对象的访问URL,请参见如何获取对象访问路径。