前面上傳檔案到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應用伺服器原理一樣。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPRplNs1WYxQ2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0ITM2ADOxYTM3EzNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
通過幾天學習,完整的實作方案如下:
一、背景建立STS使用者與帳号
需要提前在阿裡雲控制台建立好使用者資訊,并設定好權限。
1、建立通路使用者
使用者建立後,建立使用者通路的AccessKeyId和accessKeySecret,注意這兩個資訊的保護,不能洩漏出去。
2、自定義權限政策
權限政策用腳本生成,如下所示,此權限策劃包括對xiyoutianxia bucket的擷取檔案、列清單和上傳檔案權限。
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:ListObjects",
"oss:GetObject",
"oss:PutObject"
],
"Resource": [
"acs:oss:*:*:xiyoutianxia",
"acs:oss:*:*:xiyoutianxia/*"
]
}
]
}
3、建立RAM角色
建立RAM角色,并在下一面将第2條設定的權限政策賦給此角色,這一步注意複制角色權限的ARN,圖中右上。
4、将角權限制賦予給第1步建立的使用者。
注意要添加下面2個權限,一個是标準的通路STS權限,一個是自己第3步定義的通路指定bucket的權限。
二、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通路密鑰洩漏問題。