1. 程式人生 > >【上傳檔案】基於阿里雲的視訊點播VOD、物件儲存OSS實現音視訊圖片等檔案上傳

【上傳檔案】基於阿里雲的視訊點播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

}

其它都和視訊上傳一樣使用,包括上傳成功之後存的地方也是一樣的,有圖有真相:

完。