阿里云OSS回调实战(Vue + Django + DRF)

参考资料:快速搭建移动应用直传服务, Web端上传数据至OSS

摘要

近期开发过程中有OSS大文件直传(C端)并进行相应数据库操作(S端)的需求,在查找资料过程中发现相关资料较少,特此记录。

本文主要介绍了基于Vue + Django + DjangoRESTfulFramework的应用程序接入阿里云OSS的过程,主要用到的SDK有:

Browser.js,

aliyunsdkcore, aliyunsdksts

背景信息

Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS;和数据直传到OSS相比,以上方法有三个缺点:上传慢、扩展性差、费用高。基于此,本文将采用直传OSS的方式进行应用开发。

前置工作

技术方案

回调

下载SDK

WEB端:

$ npm install ali-oss

服务端:

$ pip install oss2
$ pip install aliyun-python-sdk-sts

服务端

views.py

from aliyunsdkcore import client as AliyunClient
from aliyunsdksts.request.v20150401 import AssumeRoleRequest
import oss2
import json


ENDPOINT = 'oss-cn-beijing.aliyuncs.com'    # 假设为北京节点
ACCESS_KEY_ID = 'YOUR ACCESS KEY ID'
ACCESS_KEY_SECRET = 'YOUR ACCESS KEY SECRET'
BUCKET_NAME = 'YOUR BUCKET NAME'
ROLE_ARN = 'YOUR ROLE ARN'
POLICY_TEXT = '{"Version": "1", "Statement": [{"Action": ["oss:*"], "Effect": "Allow", "Resource": ["acs:oss:*:*:YOUR BUCKET NAME/*"]}]}'

def get_sts_token():
    client = AliyunClient.AcsClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET, 'cn-beijing')
    req = AssumeRoleRequest.AssumeRoleRequest()

    req.set_accept_format('json')
    req.set_RoleArn(ROLE_ARN)
    req.set_RoleSessionName('test')
    req.set_Policy(POLICY_TEXT)
    body = client.do_action_with_exception(req)
    token = json.loads(oss2.to_unicode(body))

    return token


# 认证管理View
class STSView(views.APIView):
    authentication_classes = (AuthenticationView,)
    permission_classes = ()

    # 开发过程中可以添加认证log,在此不表
    def get(self, request):
        token = get_sts_token()
        token['Credentials']['BucketName'] = BUCKET_NAME      # 添加BucketName字段
        token['Credentials']['Endpoint'] = 'oss-cn-beijing'   # 添加Endpoint字段
        return SuccessResponse(token['Credentials'])


# echo接收到的回调参数
def callback_echo(request):
  return request.data
urls.py

...

urlpatterns = [
    path('sts/', STSView.as_view(), name='instances')
    path('callback/', callback_echo(), name='callback')
]

WEB前端代码

<template>
  <div>
    <button @click="getToken">getToken</button>      // 从服务器获取STS Token
    <input id="file" type="file" @change="onLoad"/>  // 选取文件
    <button @click="onUpload">upload</button>        // 上传文件
  </div>
</template>

<script>
export default {
  name: 'UploadCallback',
  props: {
    msg: String
  },
  data() {
    return {
      fileList: [],      // 用来记录文件信息
      credentials: null  // 用来记录从服务器获取到的token、bucket、endpoint等信息
    }
  },
  methods: {
    getToken() {
      const axios = require('axios');
      axios.post('https://server_address/api/sts/', {}, {
        headers: {
          'token': `token` // 用于认证用户身份
        }
      }).then((res) => {
        this.credentials = res.data.data
      })
    },
    onLoad(file) {
      this.fileList = file.target.files
      console.log(file.target.files[0]);
    },
    onUpload() {
      let callback = {
        url: 'http://server_address/api/callback/',
        body:'bucket=${bucket}&object=${object}&etag=${etag}'+ 
             '&size=${size}&mimeType=${mimeType}&var1=${x:var1}',
        // ${bucket}、${object}等字段是OSS规定的字段,会在处理数据过程中自动填充相应参数,详见官方文档;
        // ${x:var1}字段是用户自定义的,“x:”不可省略
        contentType: 'application/x-www-form-urlencoded', // 指定contentType
        customValue: { // 若key值在上面的body中出现(如:${x:var1}),则会将key对应的值填充在对应位置
          var1: '123123',
        }
      }
      let options = { // 上传oss的信息
        region: this.credentials.Endpoint,
        accessKeyId: this.credentials.AccessKeyId,
        accessKeySecret: this.credentials.AccessKeySecret,
        stsToken: this.credentials.SecurityToken,
        bucket: this.credentials.BucketName,
      }
            this.putObject(options, this.fileList[0], callback);
    },
    async putObject(options, data, callback) { // 上传文件
      const OSS = require('ali-oss');
      const client = new OSS(options);
      try {
        const result = await client.put('exampledir/' + data.name, data, {callback: callback});
        console.log(result);
      } catch (e) {
        console.log(e);
      }
  }
}
</script>