天天看點

VUE前端分片直傳大檔案到OSS方法

前面上傳檔案到OSS是使用的spring boot伺服器上傳,并搞定了大檔案分片上傳,但是在前後端分離的程式,使用這個方法效率存問題,使用者浏覽器先要将檔案上傳到運作VUE的nginx伺服器,再轉到spring boot應用伺服器,然後再轉到OSS伺服器,要在伺服器之間多轉一次,會影響伺服器效率。

後經同行提醒,阿裡雲OSS支援node.js SDK接口,可能在VUE伺服器上,直接通過JS接口上傳OSS伺服器。

上傳檔案到OSS伺服器,需要通路OSS的accessKeyId、accessKeySecret等重要通路密碼,直接放到node.js伺服器不安全,阿裡雲OSS提供了STS (Security Token Service) 進行臨時授權通路方案,每一次産生一個有時間期限的臨時通路Token,可以通過背景産生通路的Token,背景産生Token時先驗證使用者權限,有權限才能上傳檔案,這樣解決了檔案上傳權限問題,也避免了在前端伺服器上儲存通路資料的敏感資訊。原理如下,前端App端改為VUE應用伺服器原理一樣。

VUE前端分片直傳大檔案到OSS方法

通過幾天學習,完整的實作方案如下:

一、背景建立STS使用者與帳号

需要提前在阿裡雲控制台建立好使用者資訊,并設定好權限。

1、建立通路使用者

VUE前端分片直傳大檔案到OSS方法

使用者建立後,建立使用者通路的AccessKeyId和accessKeySecret,注意這兩個資訊的保護,不能洩漏出去。

VUE前端分片直傳大檔案到OSS方法

2、自定義權限政策

VUE前端分片直傳大檔案到OSS方法

權限政策用腳本生成,如下所示,此權限策劃包括對xiyoutianxia bucket的擷取檔案、列清單和上傳檔案權限。

{
    "Version": "1",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "oss:ListObjects",
                "oss:GetObject",
                "oss:PutObject"
            ],
            "Resource": [
                "acs:oss:*:*:xiyoutianxia",
                "acs:oss:*:*:xiyoutianxia/*"
            ]
        }
    ]
}
           

3、建立RAM角色

VUE前端分片直傳大檔案到OSS方法

建立RAM角色,并在下一面将第2條設定的權限政策賦給此角色,這一步注意複制角色權限的ARN,圖中右上。

VUE前端分片直傳大檔案到OSS方法

4、将角權限制賦予給第1步建立的使用者。

注意要添加下面2個權限,一個是标準的通路STS權限,一個是自己第3步定義的通路指定bucket的權限。

VUE前端分片直傳大檔案到OSS方法

二、Spring Boot背景實作

1、添加依賴,添加下面3個依賴

<!-- 阿裡雲OSS -->
<dependency>
   <groupId>com.aliyun.oss</groupId>
   <artifactId>aliyun-sdk-oss</artifactId>
   <version>3.8.1</version>
</dependency>
<dependency>
   <groupId>com.aliyun</groupId>
   <artifactId>aliyun-java-sdk-sts</artifactId>
   <version>3.0.0</version>
</dependency>
<dependency>
   <groupId>com.aliyun</groupId>
   <artifactId>aliyun-java-sdk-core</artifactId>
   <version>4.4.6</version>
</dependency>
           

2、設定通路參數

我是将需要保密的通路參數,放到一個專門的配置檔案,同時先報忽略上傳git,避免密鑰洩漏。

stsEndpoint: sts.cn-hangzhou.aliyuncs.com //注意與OSS的endPoint不一樣
stsAccessKeyId: ***** //第一章第1節複制的使用者accessKeyId
stsAccessKeySecret: ***** //第一章第1節複制的使用者AccessKeySecret
stsRoleArn: acs:ram::1365080985****:role/ossst**** //第一章第3節複制的ARN資訊
           

3、STS擷取實作方法,放取utils 包中,注意此處的policy内容要在第一章第2節設定的政策範圍以内,否則會報權限錯誤。

@Value("${aliyun.oss.stsEndpoint}")
private String stsEndpoint;
@Value("${aliyun.oss.stsAccessKeyId}")
private String stsAccessKeyId;
@Value("${aliyun.oss.stsAccessKeySecret}")
private String stsAccessKeySecret;
@Value("${aliyun.oss.stsRoleArn}")
private String stsRoleArn;

 /**
     * 生成OSS檔案上傳的需要的token生成
     *
     * @return STSToken
     */
    public AssumeRoleResponse getStsToken() {

        String roleSessionName = "quyouinfo";
        String policy = "{\n" +
                "    \"Version\": \"1\", \n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Action\": [\n" +
                "                \"oss:PutObject\",\n" +
                "                \"oss:GetObject\"\n" +
                "            ], \n" +
                "            \"Resource\": [\n" +
                "                \"acs:oss:*:*:xiyoutianxia\", \n" +
                "                \"acs:oss:*:*:xiyoutianxia/*\" \n" +
                "            ], \n" +
                "            \"Effect\": \"Allow\"\n" +
                "        }\n" +
                "    ]\n" +
                "}";
        try {
            // 添加endpoint(直接使用STS endpoint,前兩個參數留白,無需添加region ID)
            DefaultProfile.addEndpoint("", "", "Sts", stsEndpoint);
            // 構造default profile(參數留白,無需添加region ID)
            IClientProfile profile = DefaultProfile.getProfile("", stsAccessKeyId, stsAccessKeySecret);
            // 用profile構造client
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setMethod(MethodType.POST);
            request.setRoleArn(stsRoleArn);
            request.setRoleSessionName(roleSessionName);
            // 若policy為空,則使用者将獲得該角色下所有權限
            request.setPolicy(policy);
            // 設定憑證有效時間
            request.setDurationSeconds(1000L);
            final AssumeRoleResponse response = client.getAcsResponse(request);
            return response;
        } catch (ClientException e) {
            return null;
        }
    }
           

4、前端接口實作,注意使用者token與權限驗證,用系統統一的方案(過濾器)

/**
 * 擷取VUE前端伺服器上傳OSS需要的token
 */
@ApiOperation("擷取上傳OSS的token")
@PreAuthorize("@ss.hasPermi('data:scenicfile:edit')")
@GetMapping("/getStsToken")
public AjaxResult getStsToken() {
    AssumeRoleResponse stsToken = ossFileUtils.getStsToken();
    AjaxResult ajax = AjaxResult.success(stsToken);
    return ajax;
}
           

三、VUE前端實作

1、安裝SDK

使用npm安裝SDK開發包,安裝指令為npm install ali-oss --save。

2、使用時直接引用,不需要提前引用到全局方法

import OSS from ‘ali-oss’

3、普通檔案直接上傳,file是選取,或壓縮轉換後的檔案對象

postImage(file) {
  //建構上傳檔案參數
  this.loading = true
  let user = this.$store.getters.name
  let self = this
  //擷取上傳檔案所需要的STS Token
  getStsToken().then(response => {
    if (response.code === 200) {
      let token = response.data
      //直接通過node.js上傳
      //console.log(token)
      let client = new OSS({
        region: 'oss-cn-hangzhou',
        accessKeyId: token.credentials.accessKeyId,
        accessKeySecret: token.credentials.accessKeySecret,
        stsToken: token.credentials.securityToken,
        bucket: 'xiyoutianxia',
        secure: true
      })
      let objectName = 'scenicfile/' + self.adcode + '/' + self.form.scenicId + '/' + user  + '/'  + file.name

      async function put() {
        try {
          //object-name可以自定義為檔案名(例如file.txt)或目錄(例如abc/test/file.txt)的形式,實作将檔案上傳至目前Bucket或Bucket下的指定目錄。
          let result = await client.put(objectName, file)
          //改阿裡OSS後直接作用OSS路徑前辍,不使用系統部署路徑
          let shortUrl = result.url.substring(result.url.indexOf('scenicfile/'), result.url.length)
          self.imgUrl = process.env.VUE_APP_OSS_PREFIX + shortUrl
          self.msgSuccess('修改成功' + shortUrl)
          self.$emit('setUrl', shortUrl, self.file.name, self.location, self.takeTime)
          //console.log(result);
        } catch (e) {
          console.log(e)
        }
      }
      put()
    } else {
      this.msgError('OSS檔案上傳Token擷取不成功!')
    }
    this.loading = false
  })
},
           

四、前端大檔案分片上傳

1 大檔案分片上傳,稍微複雜一下,需要先定義一個進度條

<div>
  <el-progress :text-inside="true" :stroke-width="18" v-if="percentage" :percentage="percentage"
               v-show="isProgressVis"></el-progress>
</div>
           

2 添加進度條與loading顯示控制變量,在data中

//進度條
isProgressVis: false,
percentage: 0,
fileLoading:false,
           

3、大檔案上傳實作方法,采用的是同步執行方法

async uploadAudio(option) {
  try {
    if (this.form.scenicId !== null && this.form.scenicId !== undefined) {
      //建構上傳檔案參數
      let user = this.$store.getters.name
      this.fileLoading = true
      let self = this
      //擷取上傳檔案所需要的STS Token
      let ret = await getStsToken()
      if (ret.code != '200') {
        throw new Error('擷取OSS參數失敗')
      }
      let token = ret.data
      let client = new OSS({
        region: 'oss-cn-hangzhou',
        accessKeyId: token.credentials.accessKeyId,
        accessKeySecret: token.credentials.accessKeySecret,
        stsToken: token.credentials.securityToken,
        bucket: 'xiyoutianxia',
        secure: true
      })
      let objectName = 'scenicfile/' + self.adcode + '/' + self.form.scenicId + '/' + user  + '/' + self.file.name
      // 分片上傳檔案
      let result = await client.multipartUpload(objectName, self.file, {
	     //進度條更新
		 progress: async function(p) {
          self.percentage = parseInt(p * 100)
          self.isProgressVis = true
        }
      })
      console.log(result)
      if (result.res.statusCode === 200) {
        //改阿裡OSS後直接作用OSS路徑前辍,不使用系統部署路徑
        this.playerOptions.sources[0].src = process.env.VUE_APP_OSS_PREFIX + result.name
        this.msgSuccess('修改成功' + result.name)
        this.$emit('setUrl', result.name, this.file.name, '', '')
        this.fileLoading = false
        this.isProgressVis = false
      } else {
        this.$message.error('上傳失敗')
      }
    } else {
      this.$message.error('請先選擇景區後上傳檔案!')
    }
  } catch (error) {
    this.$message.error(error.message)
  }
}
           

五、小結

通過node.js SDK從前端應用伺服器直接上傳OSS伺服器,在大檔案上傳效率提升了很多,同時利用STS統一由後端業務伺服器做權限控制,通路Token申請,有效避免了OSS通路密鑰洩漏問題。

oss