支付宝小程序直传实践

本文介绍如何在支付宝小程序环境下将文件上传到OSS。

背景信息

小程序是当下比较流行的移动应用,例如微信小程序、支付宝小程序等。它是一种全新的开发模式,无需下载和安装,为终端用户提供更优的用户体验。如何在小程序环境下上传文件到OSS也成为开发者比较关心的一个问题。

JavaScript客户端直传实践的原理相同,小程序上传文件到OSS也是利用OSS提供的PostObject接口来实现表单文件上传到OSS。关于PostObject的详细介绍请参见PostObject

步骤1:获取签名

为了您的数据安全,建议使用签名方式上传文件。OSS提供以下两种签名方式的代码:

  • 服务端签名

    使用服务端签名时,您需要先搭建一个签名服务,之后由客户端调用签名服务生成签名。

    • 服务端签名源码

      uploadOssHelper.js代码如下:

      const crypto = require("crypto-js");
      
      class MpUploadOssHelper {
        constructor(options) {
          this.accessKeyId = options.accessKeyId;
          this.accessKeySecret = options.accessKeySecret;
          this.timeOut = options.timeout || 1; // 限制参数的生效时间(单位:小时)。
          this.maxSize = options.maxSize || 10; // 限制上传文件大小(单位:Mb)。
        }
      
        createUploadParams() {
          const policy = this.getPolicyBase64();
          const signature = this.signature(policy);
          return {
            OSSAccessKeyId: this.accessKeyId,
            policy: policy,
            signature: signature,
          };
        }
      
        getPolicyBase64() {
          let date = new Date();
          // 设置Policy过期时间。
          date.setHours(date.getHours() + this.timeOut);
          let srcT = date.toISOString();
          const policyText = {
            expiration: srcT,
            conditions: [
              // 限制上传文件大小。
              ["content-length-range", 0, this.maxSize * 1024 * 1024],
            ],
          };
          const buffer = new Buffer(JSON.stringify(policyText));
          return buffer.toString("base64");
        }
      
        signature(policy) {
          return crypto.enc.Base64.stringify(
            crypto.HmacSHA1(policy, this.accessKeySecret)
          );
        }
      }
      
      module.exports = MpUploadOssHelper;
    • 服务端接口示例

      以Express为例,接口代码如下:

      const express = require('express');
      const app = express();
      const MpUploadOssHelper = require("./uploadOssHelper.js");
      
      
      app.get('/getPostObjectParams', (req, res) => {
        const mpHelper = new MpUploadOssHelper({
          // 填写AccessKey ID。
          accessKeyId: '<Your AccessKeyId>',
          // 填写AccessKey Secret。
          accessKeySecret: '<Your AccessKeySecret>',
          // 限制参数的生效时间(单位:小时)。
          timeout: 1, 
          // 限制上传文件大小(单位:Mb)。
          maxSize: 10, 
        });
      
        // 生成参数。
        const params = mpHelper.createUploadParams();
      
        res.json(params);
      })
  • 客户端签名

    使用客户端签名时,您需要先在服务端搭建一个STS服务,之后由客户端获取STS临时授权账号并生成签名。

    • 服务端搭建STS服务

      const OSS = require('ali-oss')
      const STS = OSS.STS
      const express = require('express');
      const app = express();
      
      
      const stsClient = new STS({
        // 填写您的AccessKeyID。
        accessKeyId: '<Your AccessKeyId>',
        // 填写您的AccessKeySecret。
        accessKeySecret: '<Your AccessKeySecret>',
        // 填写Bucket名称。
        bucket: '<Your bucket name>'
      });
      
      async function getToken() {
          // 指定角色的ARN。格式为acs:ram::$accountID:role/$roleName。
          const STS_ROLE = '<STS_ROLE>'  
          const STSpolicy = {
            Statement: [
              {
                Action: ['oss:*'],
                Effect: 'Allow',
                Resource: ['acs:oss:*:*:*']
              }
            ],
            Version: '1'
          };
          const result = await stsClient.assumeRole(
            // 填写用于授权的RAM角色的ARN值。详情请参见查看RAM角色。
            STS_ROLE,
            STSpolicy,
            // 指定STS过期时间,单位为秒。
            3600 
          );
          const { credentials } = result;
      
          return credentials;
      }
      
      app.get('/getToken', async (req, res) => {
        // 获取STS。
        const credentials = await getToken()
        console.log(credentials.AccessKeyId)
        console.log(credentials.AccessKeySecret)
        console.log(credentials.SecurityToken)
        res.json(credentials);
      })
    • 客户端获取STS临时账号并生成签名

      import crypto from 'crypto-js';
      import { Base64 } from 'js-base64';
      
      // 计算签名。
      function computeSignature(accessKeySecret, canonicalString) {
        return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret));
      }
      
      const date = new Date();
      date.setHours(date.getHours() + 1);
      const policyText = {
        // 设置policy过期时间。
        expiration: date.toISOString(), 
        conditions: [
        // 限制上传的文件大小。
          ["content-length-range", 0, 1024 * 1024 * 1024],
        ],
      };
      
      async function getFormDataParams() {
        const credentials = await axios.get('/getToken')
        const policy = Base64.encode(JSON.stringify(policyText)) // policy必须为base64的string。
        const signature = computeSignature(credentials.AccessKeySecret, policy)
        const formData = {
          OSSAccessKeyId: credentials.AccessKeyId,
          signature,
          policy,
          'x-oss-security-token': credentials.SecurityToken 
        }
        return formData
      }

步骤2:使用支付宝小程序上传

支付宝小程序上传文件参考代码如下:

const host = '<host>';
const signature = '<signatureString>';
const ossAccessKeyId = '<accessKey>'; 
const policy = '<policyBase64Str>';
const securityToken = '<x-oss-security-token>';
const key = '<object name>'

my.chooseImage({
  chooseImage: 1,
  success: res => {
    const path = res.apFilePaths[0];
    my.uploadFile({
        url: host,
        fileType: 'image',
        fileName: 'file',
        filePath: path,
        formData: {
            key,
            policy,
            OSSAccessKeyId: ossAccessKeyId,
            signature,
            success_action_status: '200',
            // 使用STS签名时传入securityToken。
            // 'x-oss-security-token': securityToken 
        },
        success: (res) => {
          // 将上传成功状态码设置为200,默认状态码为204。
          if (res.statusCode === 200) {
            console.log('上传成功');
          }
          my.alert({ content: 'success info: ' + res.data });
        },
        fail: err => {
          console.log(err);
        }
    });
  }
});

相关参数如下:

  • host:填写存储空间的访问域名,例如https://test.oss-cn-zhangjiakou.aliyuncs.com。若您的存储空间已绑定自定义域名,建议填写您的自定义域名。

  • signature:填写步骤1中获取到的signature信息。

  • ossAccessKeyId:填写您的AccessKey ID,若是通过STS获取的临时用户,则填写临时用户的AccessKey ID。

  • policy:填写步骤1中获取到的policy信息。

  • key:设置文件上传至OSS后的文件路径。例如需要将myphoto.jpg上传至test文件夹下,此处填写test/myphoto.jpg。

  • securityToken:若使用STS认证,此项填写客户端签名时获取到的SecurityToken。