抖音小程序直传实践

抖音小程序直传OSS是指您在使用抖音小程序时,可以直接将内容(例如图片、视频等)上传至OSS,而不是先上传到小程序服务器再由服务器转发至OSS。通过抖音小程序直传的方式能够减轻服务器压力,提升上传速度和用户体验。

前提条件

准备工作

通过抖音小程序直传文件到OSS时,为确保上传请求的安全性,客户端不直接使用长期访问密钥,而是通过RAM用户扮演RAM角色的方式向STS服务发起请求获取临时访问凭证。

  1. 创建RAM用户。

    1. 登录RAM控制台

    2. 在左侧导航栏,选择身份管理>用户

    3. 单击创建用户

    4. 输入登录名称显示名称

    5. 访问方式区域下,选择使用永久 AccessKey 访问然后单击确定

    6. 根据界面提示,完成安全验证。

    7. 复制访问密钥(AccessKey IDAccessKey Secret)。

      重要

      RAM用户的AccessKey Secret只在创建时显示,后续不支持查看,请妥善保管。

  2. RAM用户授予请求AssumeRole的权限。

    创建RAM用户后,您需要授予RAM用户通过扮演角色来调用STS服务的权限。

    1. 单击已创建RAM用户右侧对应的添加权限

    2. 新增授权页面,选择AliyunSTSAssumeRoleAccess系统策略。

      image.png

    3. 单击确认新增授权

  3. 创建RAM角色。

    您需要创建RAM角色,用于定义RAM角色被扮演时,可以获得OSS服务的哪些访问权限。

    1. 在左侧导航栏,选择身份管理>角色

    2. 单击创建角色,选择可信实体类型为阿里云账号,单击下一步

    3. 创建角色对话框,角色名称填写为RamOssTest,选择信任的云账号当前云账号

    4. 单击完成。角色创建完成后,单击关闭

    5. 角色页面,搜索框输入角色名称RamOssTest,然后单击RamOssTest

    6. 单击ARN右侧的复制,保存角色的ARN。arn

  4. RAM角色授予上传文件的权限。

    RAM角色附加权限策略,明确RAM角色在被扮演时所能拥有的OSS资源访问权限。结合本实例教程,希望RAM用户在扮演该角色后只能向OSS指定Bucket上传文件,则需要为角色添加写入权限的策略。

    1. 创建上传文件的自定义权限策略。

      1. 在左侧导航栏,选择权限管理>权限策略

      2. 权限策略页面,单击创建权限策略

      3. 创建权限策略页面,单击脚本编辑,然后在策略文档输入框中赋予角色上传文件到examplebucket的权限。具体配置示例如下。

        {
            "Version": "1",
            "Statement": [
             {
                   "Effect": "Allow",
                   "Action": [
                     "oss:PutObject"
                   ],
                   "Resource": [
                     "acs:oss:*:*:examplebucket/*"             
                   ]
             }
            ]
        }
      4. 策略配置完成后,单击继续编辑基本信息

      5. 基本信息区域,填写策略名称RamTestPolicy,然后单击确定

    2. RAM角色RamOssTest授予自定义权限策略。

      1. 在左侧导航栏,选择身份管理 > 角色

      2. 角色页面,找到目标RAM角色RamOssTest

      3. 单击RAM角色RamOssTest右侧的新增授权

      4. 添加权限页面下的自定义策略页签,选择已创建的自定义权限策略RamTestPolicy

      5. 单击确定

方案部署

  1. 修改示例工程express-douyin.zip中的getToken.js配置。

    const crypto = require("crypto-js");
    const STS = require("ali-oss").STS;
    const { accessKeyId, secretAccessKey: accessKeySecret } = process.env;
    
    const stsClient = new STS({
      // 从环境变量中获取准备工作中创建的RAM用户访问密钥。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_IDOSS_ACCESS_KEY_SECRET。
      accessKeyId: process.env.OSS_ACCESS_KEY_ID,
      accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
      // 填写Bucket名称。
      bucket: "examplebucket",
    });
    
    async function getToken() {
      // 指定准备工作中创建的角色ARN,格式为acs:ram::$accountID:role/$roleName。
      const STS_ROLE = "acs:ram::137****:role/ramosstest";
      const STSpolicy = {
        Version: "1",
        Statement: [
          {
            Effect: "Allow",
            Action: "oss:PutObject",
            Resource: "acs:oss:*:*:examplebucket/*",
          },
        ],
      };
      const result = await stsClient.assumeRole(
        STS_ROLE,
        STSpolicy,
        3600 // STS过期时间,单位为秒。
      );
      const { credentials } = result;
    
      return credentials;
    }
    
    // 计算签名。
    function computeSignature(accessKeySecret, canonicalString) {
      return crypto.enc.Base64.stringify(
        crypto.HmacSHA1(canonicalString, accessKeySecret)
      );
    }
    const date = new Date();
    date.setHours(date.getHours() + 1);
    const policyText = {
      expiration: date.toISOString(), // 设置policy过期时间。
      conditions: [
        // 限制上传大小。
        ["content-length-range", 0, 1024 * 1024 * 1024],
      ],
    };
    
    module.exports.getToken = getToken;
    module.exports.policyText = policyText;
    module.exports.computeSignature = computeSignature;
    
  2. 获取客户端签名信息。

    1. 打开express-app.js文件,执行node express-app.js命令。

      const express = require("express");
      const app = express();
      const { Base64 } = require("js-base64");
      
      const MpUploadOssHelper = require("./uploadOssHelper.js");
      const { getToken, policyText, computeSignature } = require("./getToken");
      
      // 获取客户端签名。
      app.get("/getFormDataParams", async (req, res) => {
        // 获取STS。
        const { AccessKeySecret, AccessKeyId, SecurityToken } = await getToken();
        const policy = Base64.encode(JSON.stringify(policyText)); // policy必须为base64string。
        const signature = computeSignature(AccessKeySecret, policy);
        const formData = {
          OSSAccessKeyId: AccessKeyId,
          signature,
          policy,
          SecurityToken,
        };
        res.json(formData);
      });
      
      app.listen(3001);
      
    2. 通过浏览器输入http://localhost:3001/getFormDataParams,获取签名信息。

      {
        "OSSAccessKeyId":"STS.NTm****",
        "signature":"nS3Z****",
         "policy":"eyJleH****",
         "SecurityToken":"CAIS****"
      }
  3. 体验抖音开发者工具直传文件到OSS。

    1. 解压项目文件douyin-detail.zip

    2. 打开抖音开发者工具,导入解压后的项目。

    3. 修改page/tt-templates-article路径下index.js文件中的host配置项,其他选项保留默认配置。

      const app = getApp();
      const timeago = require("./timeago.js");
      
      import { templateDataValidator } from "./validator";
      // const STS = require("ali-oss").STS;
      // 填写Bucket外网Endpoint。
      const host = 'https://examplebucket.oss-cn-hangzhou.aliyuncs.com'; 
      
      
      Page({
        onLoad(options) {
        },
        
        chooseImage: function() {
          var that = this;
          // 选择图片。
          tt.chooseImage({
            count: 1, // 设置图片的数量,默认值是9。
            sizeType: ['original', 'compressed'], // 指定上传原图或者压缩图,默认二者均可。
            sourceType: ['album', 'camera'], // 指定上传来源是相册还是相机,默认二者均可。
            success: function(res) {
              console.log('原始', res);
              // 成功选择图片后,获取图片的本地文件路径。
              // var localIds = res.tempFiles;
              var localIds = res.tempFilePaths;
              // 使用uploadFile上传图片。
              that.uploadImage(localIds[0]);
            }
          });
        },
        uploadImage: function(path) {
          const signature = 'signatureString';
          const ossAccessKeyId = 'accessKey';
          const key = ''; 
          const securityToken = 'x-oss-security-token'; 
      
          // 获取token等相关信息。
          tt.request({
            url:'http://localhost:3001/getFormDataParams',
            success: function(res) {
              const {OSSAccessKeyId, policy, signature, SecurityToken} = res.data;
              console.log('SecurityToken', SecurityToken);
              // 上传图片。
              tt.uploadFile({
                url: host, 
                filePath: path,
                name: 'file',
                formData: {
                  key:  path.split('/').pop(), // 设置文件上传至OSS后的文件路径。
                  policy,
                  OSSAccessKeyId,
                  signature,
                  // success_action_status: '200',
                  // 使用STS签名时传入securityToken。
                  'x-oss-security-token': SecurityToken 
                },
                success: function(res) {
                  // 上传成功后,服务器返回的相关信息。
                  var serverId = res.serverId; // 返回服务器ID。
                  console.log('图片上传成功,服务器ID:', res);
                  // 将serverId发送到服务器端执行保存等操作。
                },
                fail: function(err) {
                  console.log('图片上传失败:', err);
                }
              });
      
            },
            fail: function(err) {
            }
          });
        }
      });
    4. 选择图片,然后上传到OSS。

      image