【上傳檔案】基於阿里雲的視訊點播VOD、物件儲存OSS實現音視訊圖片等檔案上傳
一、效果演示
a.測試介面初始化
b.點選【上傳視訊】
c.點選【開始上傳】
d.點選【上傳音訊】選擇音訊之後點選【開始上傳】
c.點選【上傳圖片】,注意圖片上傳為單按鈕,選擇檔案之後自動完成上傳
d.點選【確定】來獲取以上幾個上傳檔案成功之後返回的最關鍵的key值
注:其中音視訊我們取其videoId,圖片取url
二、測試頁面程式碼
此處,先展示上方測試頁面對已封裝好的上傳元件的引用程式碼,如下:
<template> <div> <h5>上傳視訊元件測試</h5> <div> <video-upload v-model="uploadVideoId"></video-upload> </div> <h5>上傳音訊元件測試</h5> <div> <audio-upload v-model="uploadAudioId"></audio-upload> </div> <h5>上傳圖片元件測試</h5> <div> <img-upload v-model="uploadImgUrl"></img-upload> </div> <h5>獲取上傳成功的值</h5> <el-button size="small" type="primary" @click="getVal">確定</el-button> <div class="msgContent" v-if="msg" v-html="msg"></div> </div> </template> <script> import videoUpload from '@/components/videoUpload' import audioUpload from '@/components/audioUpload' import imgUpload from '@/components/imgUpload' export default { data() { return { uploadVideoId: '', uploadAudioId: '', uploadImgUrl: '', msg: '' } }, components: { videoUpload, audioUpload, imgUpload }, methods: { getVal() { this.msg = ` this.uploadVideoId is : <br> ${this.uploadVideoId} <br> this.uploadAudioId is : <br> ${this.uploadAudioId} <br> this.uploadImgUrl is : <br> ${this.uploadImgUrl}` } } } </script> <style lang="less" scoped> h5 { margin: 36px 0 12px 0; } .msgContent { margin-top: 10px; font-size: 14px; color: #606266; line-height: 24px; } </style>
主要實現的上傳元件為圖片上傳(imgUpload)、視訊上傳(videoUpload)、音訊上傳(audioUpload),其中圖片上傳的元件是基於阿里雲物件儲存(OSS) 來實現的,而音視訊上傳都是基於阿里雲視訊點播(VOD)實現的,具體實現方式見下文。
三、圖片上傳
相對音視訊的上傳,圖片上傳的方式會比較簡單,我們先介紹圖片上傳元件的實現方式吧。首先我們參考的是node.js環境下的上傳檔案最簡單的方式,即通過put介面來上傳一個本地檔案到OSS,至於流式上傳、分片上傳、斷點上傳等相對較複雜的上傳方式此處不予介紹,反正舉一反三,都是差不多的套路。
第一步,安裝SDK
注:支援的Node.js版本: Node.js >= 8.0.0 如果需要在 Node.js < 8 的環境中使用,請使用 ali-oss 4.x版本。
npm install ali-oss
第二步,使用SDK
通過初始化一個client,然後呼叫其put方法即可。
官網給出的關鍵程式碼:
let OSS = require('ali-oss') let client = new OSS({ region: '<Your region>', accessKeyId: '<Your AccessKeyId>', accessKeySecret: '<Your AccessKeySecret>', bucket: 'Your bucket name' }); async function put () { try { let result = await client.put('object-name', 'local-file'); console.log(result); } catch (e) { console.log(er); } } put();
結合我們的實際應用場景,我們在new OSS的時候會多傳一個stsToken引數,stsToken的值由後端介面給我們提供,此處我們取的是後端返回的securityToken,其作用主要用於進行臨時授權訪問,可以參考官方文件:使用STS訪問
另外,基於這位SDK大兄弟的功能我們結合產品需求實現了以下幾點額外功能:
a、不要醜陋的原生input按鈕,自定義上傳按鈕;
b、上傳限制僅圖片格式;
c、上傳成功之後展示已上傳的縮圖;
d、元件的v-model值用於儲存圖片上傳成功後的url。
OK,然後我們欣賞一下 imgUpload.vue 的實現程式碼:
<template>
<div>
<input type="file" v-show="false" accept="image/*" ref="input" @change="fileChange">
<!-- 通過$refs操作input的點選事件,調起檔案選擇 -->
<el-button size="small" type="success" @click="$refs.input.click()">
上傳圖片
</el-button>
<img v-if="imgUrl" class="imgShow" :src="imgUrl"/>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
uploadFile: null,
imgUrl: ''
}
},
watch: {
imgUrl: function (val) {
if (val !== this.value) {
this.$emit('input', val)
}
}
},
methods: {
fileChange() {
if (!event.target.files[0]) {
return false
}
console.log(event.target.files[0])
if (!event.target.files[0].type.match('image.*')) {
this.$message.error('請選擇圖片')
return false
}
this.uploadFile = event.target.files[0]
this.$apis.getImageUploadInfo({'fileName': this.uploadFile.name}).then(res => {
if (res.code === '2000') {
let objectName = res.data.key
let OSS = require('ali-oss')
let client = new OSS({
region: res.data.regionId,
accessKeyId: res.data.accessKeyId,
accessKeySecret: res.data.accessKeySecret,
stsToken: res.data.securityToken,
bucket: res.data.bucket
})
const put = async() => {
try {
let result = await client.put(objectName, this.uploadFile)
console.log(result)
if (result.res.statusCode === 200) {
this.$message.success('上傳成功')
this.imgUrl = result.url
} else {
this.$message.success('上傳失敗')
this.imgUrl = ''
}
} catch (e) {
this.$message.error(e)
console.log(e)
}
}
put()
} else {
this.$message.error(res.message)
}
}).catch(error => {
this.$message.error(error)
})
}
}
}
</script>
<style lang="less" scoped>
.imgShow {
display: block;
height: 100px;
margin: 12px 0;
}
</style>
後臺截圖:
四、視訊上傳
其實基於上面那位OSS大兄弟的強大儲存功能我們也可以實現視訊的上傳儲存,但是需求方對於視訊的處理要求比較過分,哦不,是比較精緻。所以,我們採用阿里雲視訊點播來實現對視訊的上傳與播放。阿里雲視訊點播(VOD)是集音視訊採集、編輯、上傳、自動化轉碼處理、媒體資源管理、分發加速、視訊播放於一體的一站式音視訊點播解決方案。知道它的厲害了吧~
第一步,安裝SDK
相對於OSS大兄弟,VOD的安裝比較土了,不,是比較簡單了,在我們的頁面上引入三個JS指令碼,指令碼下載:視訊點播-客戶端SDK下載
引入程式碼,官方demo:
<!-- IE需要es6-promise -->
<script src="../lib/es6-promise.min.js"></script>
<script src="../lib/aliyun-oss-sdk4.13.2.min.js"></script>
<script src="../aliyun-vod-upload-sdk1.3.1.min.js"></script>
實際應用中,我們會在index.html的屁股後面引入這三位大兄弟,如下:
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<!-- 引入第三方庫-阿里雲視訊上傳 start-->
<script src="./static/lib/es6-promise.min.js"></script>
<script src="./static/lib/aliyun-oss-sdk-5.2.0.min.js"></script>
<script src="./static/lib/aliyun-upload-sdk-1.4.0.min.js"></script>
<!-- 引入第三方庫-阿里雲視訊上傳 end-->
</body>
ps:為什麼不放<head>裡邊引入,我就不解釋了。
第二步,使用SDK
使用過程中,推薦參考上方下載好的示例程式碼,裡邊註釋給的也很清楚,相比較官網文件的話會更好理解。
官網給出的關鍵程式碼:
var uploader = new AliyunUpload.Vod({
//分片大小預設1M,不能小於100K
partSize: 1048576,
//並行上傳分片個數,預設5
parallel: 5,
//網路原因失敗時,重新上傳次數,預設為3
retryCount: 3,
//網路原因失敗時,重新上傳間隔時間,預設為2秒
retryDuration: 2,
// 開始上傳
'onUploadstarted': function (uploadInfo) {
log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object);
//上傳方式1, 需要根據uploadInfo.videoId是否有值,呼叫點播的不同介面獲取uploadauth和uploadAddress,如果videoId有值,呼叫重新整理視訊上傳憑證介面,否則呼叫建立視訊上傳憑證介面
// uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId);
//上傳方式2
// uploader.setSTSToken(uploadInfo, accessKeyId, accessKeySecret,secretToken);
}
// 檔案上傳成功
'onUploadSucceed': function (uploadInfo) {
log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object);
},
// 檔案上傳失敗
'onUploadFailed': function (uploadInfo, code, message) {
log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message);
},
// 檔案上傳進度,單位:位元組
'onUploadProgress': function (uploadInfo, totalSize, loadedPercent) {
log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(loadedPercent * 100) + "%");
},
// 上傳憑證超時
'onUploadTokenExpired': function (uploadInfo) {
console.log("onUploadTokenExpired");
//上傳方式1 實現時,根據uploadInfo.videoId呼叫重新整理視訊上傳憑證介面重新獲取UploadAuth
// uploader.resumeUploadWithAuth(uploadAuth);
// 上傳方式2 實現時,從新獲取STS臨時賬號用於恢復上傳
// uploader.resumeUploadWithSTSToken(accessKeyId, accessKeySecret, secretToken, expireTime);
},
//全部檔案上傳結束
'onUploadEnd':function(uploadInfo){
log("onUploadEnd: uploaded all the files");
}
});
從例子中可知,這位大兄弟有兩種上傳方式:VodMode 、STSMode,使用上兩者需要的上傳憑證是不同的,接下來我們介紹的是STS模式,該模式下需要後端給我們的值有:securityToken、accessKeyId、accessKeySecret
另外,基於這位SDK大兄弟的功能我們結合產品需求實現了以下幾點額外功能:
a、不要醜陋的原生input按鈕,自定義上傳按鈕;
b、上傳限制僅視訊格式及視訊大小;
c、上傳過程中顯示進度條效果;
d、元件的v-model值用於儲存視訊上傳成功後的videoId。
OK,然後我們欣賞一下 videoUpload.vue 的實現程式碼:
<template>
<div>
<input type="file" v-show="false" accept="video/*" ref="input" @change="fileChange">
<el-button size="small" type="primary" @click="$refs.input.click()" :disabled="disableUpload">
上傳視訊
</el-button>
<el-button style="margin-left: 10px;" size="small" type="success" @click="uploader.startUpload()" :disabled="disableSubmit">
開始上傳
</el-button>
<div class="el-upload__tip">視訊檔案僅限5分鐘以內,大小不超過150M</div>
<template v-if="uploadFile && uploadFile.name">
<div class="el-upload__tip">{{uploadFile.name}}</div>
<el-progress style="width:200px;" :percentage="percentage" :status="percentage === 100 ? 'success' : ''"></el-progress>
</template>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
securityToken: '',
accessKeyId: '',
accessKeySecret: '',
disableUpload: false,
disableSubmit: true,
uploadFile: null,
uploader: null,
callback: null,
videoId: '',
percentage: 0
}
},
watch: {
videoId: function (val) {
if (val !== this.value) {
this.$emit('input', val)
}
}
},
methods: {
fileChange() {
if (!event.target.files[0]) {
return false
}
console.log(event.target.files[0])
if (!event.target.files[0].type.match('video.*')) {
this.$message.error('請選擇視訊檔案')
return false
}
this.uploadFile = event.target.files[0]
this.percentage = 0
// 上傳檔案的size的單位為 位元組(b)
// 150兆位元組(mb) = 157286400位元組(b)
if (this.uploadFile.size <= 157286400) {
this.getAuth(() => {
// 給uploader大佬addFile
this.getUploder()
// 以下三個引數預設為空
// var endpoint = ''
// var bucket = ''
// var objectPre = ''
// if(objectPre)
// {
// object = objectPre +"/"+ event.target.files[i].name
// }
// STS的上傳方式,需要在userData裡指定Title
var userData = '{"Vod":{"StorageLocation":"","Title":"' + this.uploadFile.name + '","Description":"預設描述資訊暫無","CateId":"19","Tags":"測試視訊"}}'
this.uploader.addFile(this.uploadFile, '', '', '', userData)
console.log(`add file: ${this.uploadFile.name}`)
this.disableSubmit = false
})
} else {
this.disableSubmit = true
this.$message.error('上傳的檔案大小超過150M,請重新上傳')
}
},
getAuth(callback) {
// 獲取上傳憑證
this.$apis.getVideoUploadAuth().then(res => {
if (res.code === '2000') {
this.securityToken = res.data.securityToken
this.accessKeyId = res.data.accessKeyId
this.accessKeySecret = res.data.accessKeySecret
callback()
} else {
this.$message.error(res.message)
}
}).catch(error => {
this.$message.error(error)
})
},
getUploder() {
let _this = this
/* eslint-disable no-undef */
_this.uploader = new AliyunUpload.Vod({
// 檔案上傳失敗
'onUploadFailed': function (uploadInfo, code, message) {
_this.$message.error(`檔案上傳失敗:${message}`)
console.log(`onUploadFailed: file:${uploadInfo.file.name},code:${code}, message:${message}`)
_this.disableSubmit = true
_this.disableUpload = false
},
// 檔案上傳完成
'onUploadSucceed': function (uploadInfo) {
console.log(uploadInfo)
// console.log(`onUploadSucceed: ${uploadInfo.file.name}, endpoint:${uploadInfo.endpoint}, bucket:${uploadInfo.bucket}, object:${uploadInfo.object}`)
_this.videoId = uploadInfo.videoId
_this.$message.success('檔案上傳成功')
_this.disableSubmit = true
_this.disableUpload = false
},
// 檔案上傳進度
'onUploadProgress': function (uploadInfo, totalSize, loadedPercent) {
_this.percentage = +(loadedPercent * 100).toFixed(0)
// console.log(`onUploadProgress:file:${uploadInfo.file.name}, fileSize:${totalSize}, percent:${(loadedPercent * 100.00).toFixed(2)}%`)
},
// STS臨時賬號會過期,過期時觸發函式
'onUploadTokenExpired': function (uploadInfo) {
console.log('onUploadTokenExpired STS臨時賬號過期了')
// 實現時,從新獲取STS臨時賬號用於恢復上傳
// uploader.resumeUploadWithSTSToken(accessKeyId, accessKeySecret, securityToken, expireTime)
},
onUploadCanceled: function(uploadInfo) {
console.log(`onUploadCanceled:file:${uploadInfo.file.name}`)
},
// 開始上傳
'onUploadstarted': function (uploadInfo) {
var accessKeyId = _this.accessKeyId
var accessKeySecret = _this.accessKeySecret
var securityToken = _this.securityToken
_this.uploader.setSTSToken(uploadInfo, accessKeyId, accessKeySecret, securityToken)
console.log(`onUploadStarted:${uploadInfo.file.name},endpoint:${uploadInfo.endpoint}, bucket:${uploadInfo.bucket}, object:${uploadInfo.object}`)
},
'onUploadEnd': function(uploadInfo) {
console.log('onUploadEnd: uploaded all the files')
}
})
}
}
}
</script>
後臺截圖:
五、音訊上傳
音訊上傳實際上用的就是視訊上傳那套邏輯,程式碼也是ctrl+c、ctrl+v過去的,主要改動:
a、把按鈕文字“上傳視訊”改為“上傳音訊”
b、限制視訊格式改為限制音訊.mp3格式
<input type="file" v-show="false" accept="video/*" ref="input" @change="fileChange">
改為
<input type="file" v-show="false" accept="audio/mpeg" ref="input" @change="fileChange">
c、js部分
if (!event.target.files[0].type.match('video.*')) {
this.$message.error('請選擇視訊檔案')
return false
}
改為
if (!event.target.files[0].type.match('audio.*')) {
this.$message.error('請選擇音訊檔案')
return false
}
其它都和視訊上傳一樣使用,包括上傳成功之後存的地方也是一樣的,有圖有真相:
完。