
# 小程序直传OBS
#### 背景信息
微信小程序作为当下流行的移动应用，具有广泛的应用场景。如何通过微信小程序上传文件至对象存储服务OBS成为了一个热点问题，本文将通过一个示例程序进行演示。
#### 注意事项
- 客户端计算签名时依赖引用"crypto-js"及"js-base64"两个开源组件，因此需要在微信小程序项目中设置使用NPM模块。
- 在微信小程序中进行编译时，如果在引入"crypto-js"包时出现"Maximum call stack size exceed"报错，请升级微信小程序开发客户端至最新版本。
- 上传过程中返回405时，请检查指定的endpoint是否为对应上传桶的桶域名。
 
#### 操作步骤
1. **设置桶的跨域访问权限。**
   
   微信小程序基于BrowserJS进行开发，受同源安全策略的要求，不同域间的网站脚本和内容如需交互，需要配置跨域资源共享（CORS）规范。华为云对象存储服务OBS支持CORS规范，允许跨域访问OBS中的资源，具体配置步骤请参见[配置跨域资源共享](https://support.huaweicloud.com/usermanual-obs/zh-cn_topic_0066036542.html)。
   CORS规则配置项建议：
   表1CORS规则 
   | 参数    | 说明                                                                                                                                                                                                                                                               | 配置建议                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
   |:---|:---|:---|
   | 允许的来源 | 必选参数，指定允许的跨域请求的来源，即允许来自该域名下的请求访问该桶。 允许多条匹配规则，以回车换行为间隔。每个匹配规则允许使用最多一个"\*"通配符。例如： ``` 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秒。                                                                                                                                                                                                                        | 根据实际业务设置。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
      
   
   
2. **配置小程序上传域名白名单。**
   
   微信小程序利用白名单机制管理跨域访问，想要实现数据上传，需要在微信小程序平台域名白名单中配置桶的访问域名。
   1. 获取桶的访问域名。 在桶列表单击待操作的桶，进入对象页面后单击"概览"。在"域名信息"下查看桶的访问域名。
      图1桶的访问域名   
      ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001683016845.png "点击放大")
      
      
   
   2. 在微信小程序服务器域名配置中指定桶域名为合法域名。详细配置指导请在[小程序客服](https://kf.qq.com/product/wx_xcx.html)搜索"服务器域名配置"，配置信息如[表2]所示。
      图2微信小程序配置服务器信息   
      ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001150823496.png "点击放大")
       表2微信小程序配置建议 
      | 参数               | 配置建议     |
      |:---|:---|
      | request合法域名      | 桶的访问域名   |
      | socket合法域名       | 根据实际情况填写 |
      | uploadFile合法域名   | 桶的访问域名   |
      | downloadFile合法域名 | 桶的访问域名   |
      | udp合法域名          | 根据实际情况填写 |
      | tcp合法域名          | 根据实际情况填写 |
         
      
   
   
   
   
3. **计算POST上传签名。**
   
   POST上传前需要根据上传时自定义使用的policy字段计算相关签名信息，签名计算规则请参见[基于浏览器上传的表单中携带签名](https://support.huaweicloud.com/api-obs/obs_04_0012.html)，计算签名相关源代码如下：
   对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;
   ```
   
   
4. **使用小程序直传数据至对象存储桶中。**
   
   基于[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](https://support.huaweicloud.com/api-iam/iam_04_0002.html)。
     ```
     // 指定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;
     ```
     
   
   
   ![](https://support.huaweicloud.com/bestpractice-obs/public_sys-resources/note_3.0-zh-cn.png)
   配置文件中传入的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;
   ```
   
   
 
#### 相关操作
上传完成后，要获取对应对象的访问URL，请参见[如何获取对象访问路径](https://support.huaweicloud.com/obs_faq/obs_faq_0031.html)。
