vue3 + el-upload组件集成阿里云视频点播从本地上传至点播存储
阿里云官方文档
使用JavaScript SDK上传文件_视频点播(VOD)-阿里云帮助中心 (aliyun.com)
实现方式有多种 我使用的是 上传地址和凭证方式 及 阿里云JavaScript SDK 这种上传方式
首先下载并引用阿里云的sdk,建议放在 public 文件夹中,在入口文件 index.html 导入
检查阿里云上传SDK是否加载成功
<script>// 检查SDK是否加载成功document.addEventListener('DOMContentLoaded',function() {if (window.AliyunUpload) {console.log('阿里云上传SDK加载成功');} else {console.error('阿里云上传SDK加载失败');}});
</script>
我这里采用封装组件形式使用
template代码
<template><div><el-uploadref="uploadRef"class="upload-video"dragaction="noop"multiple:limit="1":disabled="uploadDisabled":show-file-list="false":http-request="fileChange"><div class="flex flex-col justify-center items-center"><el-imageclass="block! w-86px h-64px mb-4px":src="importImage('upload_icon', 'icon')"fit="scale-down"/><span class="text-14px color-#999">支持格式:MAP4</span></div></el-upload><transition name="el-fade-in"><div v-if="fileData"><!-- <el-button :disabled="uploadDisabled" type="primary" @click="authUpload">开始上传</el-button> --><el-progressclass="my-12px":text-inside="true":percentage="authProgress"stripedstriped-flow:duration="12"/><div class="flex justify-end items-center"><el-button :disabled="pauseDisabled" type="warning" @click="pauseUpload">暂停上传</el-button><el-button :disabled="resumeDisabled" type="success" @click="resumeUpload">继续上传</el-button><el-popconfirm title="确认删除" placement="top"><template #reference><el-button :disabled="resumeDisabledDel" type="danger"> 删除文件 </el-button></template><template #actions="{ cancel }"><el-button type="" text size="small" @click="cancel">取消</el-button><el-buttonclass="bg-gradient-primary color-#fff!"size="small"@click="handleResumeDel">确定</el-button></template></el-popconfirm></div></div></transition></div>
</template>
script代码
定义并初始化相关变量
<script setup lang="ts">
import { createUploadVideo, refreshUploadVideo } from '@/api/upload' //后端提供的接口
import { to } from 'await-to-js' // 无需 try-catch 即可轻松处理错误的异步等待包装器
import type { ElUpload } from 'element-plus'// 上传组件实例
const uploadRef = ref<InstanceType<typeof ElUpload> | null>(null)// 上传的视频信息
const fileData = ref<{type: string
} | null>(null)// 上传实例
const uploader = ref<any>(null)
// 上传进度
const authProgress = ref(0)// 按钮状态管理
const startDisabled = ref(true) // 开始按钮
const pauseDisabled = ref(true) // 暂停按钮
const resumeDisabled = ref(true) // 继续按钮
const resumeDisabledDel = ref(true) // 删除按钮
const uploadDisabled = ref(false) // 控制上传组件上传// 向父组件传递 阿里媒介id 参数
const emit = defineEmits(['uploadSuccess', 'resumeDel'])// 批量导入静态资源图片
const importImage = computed(() => (name: string, folder = '', type = 'png') => {const glob = import.meta.glob<string>(`@/assets/**/*.{png,svg,jpg,jpeg}`, {eager: true,query: '?url',import: 'default',})return glob[`/src/assets/${folder}/${name}.${type}`]
})
</script>
上传视频前处理
const fileChange = (e: { file: { type: string } | null }) => {return new Promise<void>((resolve, reject) => {// console.log(e)// 检查文件是否存在if (!e || !e.file) {ElMessage.warning('请先选择需要上传的文件!')return}const ALLOWED_TYPES = ['mp4', 'avi', 'mov']// 验证文件类型if (!ALLOWED_TYPES.includes(e.file.type.split('/')[1])) {ElMessage.error(`不支持的文件格式!请上传以下格式之一:${ALLOWED_TYPES.join('、')}`)return false}// 保存选择的文件fileData.value = e.file// 获取上传的参数const userData = '{"Vod":{}}'// 如果已有上传实例,停止当前上传/* if (uploader.value) {uploader.value.stopUpload()authProgress.value = 0} */// 始化一个 uploaderuploader.value = createUploader()try {uploader.value.addFile(fileData.value, null, null, null, userData)// ElMessage.success('文件已添加,等待上传...')// authUpload()resolve()} catch (error) {console.error('添加文件失败:', error)ElMessage.error('文件添加失败,请重试')reject(error)}})
}
创建上传实例(也可自行结合官方文档调整)
const createUploader = () => {const uploader = new AliyunUpload.Vod({// userID,用于标识上传者的身份,必填,有值即可,可以是阿里云账号ID或者您自定义的用户ID,您可以访问阿里云账号中心(https://account.console.aliyun.com/)查看账号IDuserId: 'xxx',// 上传到视频点播的地域,默认值为'cn-shanghai',//eu-central-1,ap-southeast-1region: 'cn-shenzhen',// 分片大小默认1 MB,不能小于100 KB(100*1024)partSize: Math.round(1048576),// 并行上传分片个数,默认5parallel: 5,// 网络原因失败时,重新上传次数,默认为3retryCount: 3,// 网络原因失败时,重新上传间隔时间,默认为2秒retryDuration: 2,timeout: 60000,localCheckpoint: true, //此参数是禁用服务端缓存,不影响断点续传// 文件添加成功回调addFileSuccess: function (uploadInfo) {startDisabled.value = false // 启用开始上传按钮resumeDisabled.value = true // 禁用继续上传按钮ElMessage.success('文件添加成功, 等待上传...')authUpload()// console.log('addFileSuccess: ' + uploadInfo.file.name)},// 开始上传onUploadstarted: async (uploadInfo: { videoId: string; file: { name: string } }) => {// console.log('🚀 ~ createUploader ~ uploadInfo:', uploadInfo)// 如果是 UploadAuth 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法// 如果是 UploadAuth 上传方式, 需要根据 uploadInfo.videoId是否有值,调用点播的不同接口获取uploadauth和uploadAddress// 如果 uploadInfo.videoId 有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口// 直接调用了获取 UploadAuth 的测试接口, 用户在使用时需要判断 uploadInfo.videoId 存在与否从而调用 openApi// 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)// 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)if (!uploadInfo.videoId) {console.log('没有视频ID,调用创建视频上传凭证接口')const fileName = uploadInfo.file.name.replace(/\.[^/.]+$/, '')const data = {fileurl: '/Users/elt/Downloads/美食.mp4',// title: fileName,title: fileName + new Date().getTime(),}const [err, res] = await to(createUploadVideo(data))if (err) {pauseDisabled.value = trueresumeDisabled.value = trueuploadDisabled.value = falseuploader.value = nullfileData.value = nulluploadRef.value?.clearFiles()return}const { UploadAuth, UploadAddress, VideoId } = res.datauploader.setUploadAuthAndAddress(uploadInfo, UploadAuth, UploadAddress, VideoId)// console.log('🚀 ~ createUploader ~ res:', res)ElMessage.success('文件开始上传...')} else {const { videoId } = uploadInfoconst [err, res] = await to(refreshUploadVideo({ video_id: videoId }))if (err) {pauseDisabled.value = trueresumeDisabled.value = trueuploadDisabled.value = falseuploader.value = nullfileData.value = nulluploadRef.value?.clearFiles()return}const { UploadAuth, UploadAddress } = res.datauploader.setUploadAuthAndAddress(uploadInfo, UploadAuth, UploadAddress, videoId)// console.log('🚀 ~ createUploader ~ res:', res)ElMessage.success('文件开始上传...')}},// 文件上传成功onUploadSucceed: (uploadInfo: { videoId: string }) => {const { videoId } = uploadInfoElMessage.success('文件上传成功')startDisabled.value = truepauseDisabled.value = trueresumeDisabled.value = trueuploadDisabled.value = falseresumeDisabledDel.value = falseuploadRef.value?.clearFiles()emit('uploadSuccess', videoId)},// 文件上传失败onUploadFailed: (uploadInfo: any, code: any, message: string) => {ElMessage.error(`上传失败: ${message}`)pauseDisabled.value = true // 禁用暂停按钮resumeDisabled.value = false // 启用继续按钮uploadDisabled.value = trueresumeDisabledDel.value = false},// 上传取消回调onUploadCanceled: (uploadInfo) => {console.log('文件已取消上传')ElMessage.warning('文件已暂停上传')pauseDisabled.value = true // 禁用暂停按钮resumeDisabled.value = false // 启用继续按钮},// 文件上传进度,单位:字节onUploadProgress: (uploadInfo: any, totalSize: any, loadedPercent: number) => {// console.log('文件上传进度: ' + loadedPercent)const progressPercent = Math.ceil(loadedPercent * 100)authProgress.value = progressPercent},// 上传凭证或STS token超时onUploadTokenExpired: async (uploadInfo: { videoId: string }) => {const { videoId } = uploadInfoconst [err, res] = await to(refreshUploadVideo({ video_id: videoId }))if (err) {ElMessage.error('凭证刷新失败,上传暂停')pauseDisabled.value = trueresumeDisabled.value = falseresumeDisabledDel.value = falsereturn}const { uploadAuth } = res.datauploader.value.resumeUploadWithAuth(uploadAuth)ElMessage.success('凭证已刷新,继续上传...')console.log('🚀 ~ createUploader ~ res:', res)},// 全部文件上传结束onUploadEnd: (uploadInfo) => {console.log('🚀 ~ onUploadEnd ~ uploadInfo:', uploadInfo)},})return uploader
}
按钮相关操作(开始、暂停、恢复、删除)
点击开始按钮上传文件
注意:(项目中采用自动上传模式,暂时未启用该按钮,但是需要调用到此方法)
const authUpload = () => {if (uploader.value && fileData.value) {uploader.value.startUpload()startDisabled.value = true // 禁用开始按钮pauseDisabled.value = false // 启用暂停按钮resumeDisabled.value = true // 禁用继续按钮uploadDisabled.value = true // 上传时不能再次点击上传}
}
暂停上传
const pauseUpload = () => {if (uploader.value) {uploader.value.stopUpload()resumeDisabled.value = falsepauseDisabled.value = trueresumeDisabledDel.value = falseElMessage.warning('已暂停上传')}
}
恢复上传
const resumeUpload = () => {if (uploader.value) {uploader.value.startUpload()resumeDisabled.value = truepauseDisabled.value = falseElMessage.success('已恢复上传')}
}
删除本地上传文件及清空上传列表及上传实例
const handleResumeDel = () => {uploadRef.value?.clearFiles()emit('resumeDel')pauseDisabled.value = true // 禁用暂停按钮resumeDisabled.value = true // 禁用继续按钮uploadDisabled.value = false // 启用上传组件fileData.value = nullauthProgress.value = 0uploader.value.stopUpload()uploader.value = null
}
暴露给父组件
defineExpose({uploadDisabled,resumeDisabledDel,handleResumeDel,
})
html代码
<template><div class="home bg-#F2F4F5"><p class="text-16px font-bold color-#333 pt-20px ml-24px box-border">上传视频</p><!-- ---uploadSuccess、resumeDel 就是子组件暴露的方法 这里不多解释 自行定义就行--- --><UploadVideo ref="uploadVideoRef" class="mt-16px mx-24px" @uploadSuccess="handleUploadSuccess"@resumeDel="handleResumeDel" /></div>
</template>
css代码
<style scoped lang="scss">
.upload-video {::v-deep(.el-upload) {.el-upload-dragger {display: flex;justify-content: center;align-items: center;height: 192px;padding: 0;background-color: #f2f4f5;border-color: #999;}}
}::v-deep(.el-progress) {.el-progress-bar__outer {height: 16px !important;.el-progress-bar__inner {background-color: #2180ec;.el-progress-bar__innerText {display: block;line-height: 16px;}}}
}
</style>
未上传前效果
上传成功效果