抖音小程序直传实践

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

前提条件

准备工作

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

  1. 创建RAM用户。

    1. 登录RAM控制台

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

    3. 单击创建用户

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

    5. 访问方式区域下,选择OpenAPI调用访问,然后单击确定

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

    7. 复制访问密钥(AccessKey ID和AccessKey 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_ID和OSS_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必须为base64的string。
        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