
实现方式有多种 我使用的是 上传地址和凭证方式 及 阿里云JavaScript SDK 这种上传方式
首先下载并引用阿里云的sdk,建议放在 public 文件夹中,在入口文件 index.html 导入
检查阿里云上传SDK是否加载成功
javascript
<script>
// 检查SDK是否加载成功
document.addEventListener('DOMContentLoaded',
function() {
if (window.AliyunUpload) {
console.log('阿里云上传SDK加载成功');
} else {
console.error('阿里云上传SDK加载失败');
}
});
</script>
我这里采用封装组件形式使用
template代码
html
<template>
<div>
<el-upload
ref="uploadRef"
class="upload-video"
drag
action="noop"
multiple
:limit="1"
:disabled="uploadDisabled"
:show-file-list="false"
:http-request="fileChange"
>
<div class="flex flex-col justify-center items-center">
<el-image
class="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-progress
class="my-12px"
:text-inside="true"
:percentage="authProgress"
striped
striped-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-button
class="bg-gradient-primary color-#fff!"
size="small"
@click="handleResumeDel"
>
确定
</el-button>
</template>
</el-popconfirm>
</div>
</div>
</transition>
</div>
</template>
script代码
定义并初始化相关变量
javascript
<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>
上传视频前处理
javascript
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
} */
// 始化一个 uploader
uploader.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)
}
})
}
创建上传实例(也可自行结合官方文档调整)
javascript
const createUploader = () => {
const uploader = new AliyunUpload.Vod({
// userID,用于标识上传者的身份,必填,有值即可,可以是阿里云账号ID或者您自定义的用户ID,您可以访问阿里云账号中心(https://account.console.aliyun.com/)查看账号ID
userId: 'xxx',
// 上传到视频点播的地域,默认值为'cn-shanghai',
//eu-central-1,ap-southeast-1
region: 'cn-shenzhen',
// 分片大小默认1 MB,不能小于100 KB(100*1024)
partSize: Math.round(1048576),
// 并行上传分片个数,默认5
parallel: 5,
// 网络原因失败时,重新上传次数,默认为3
retryCount: 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 = true
resumeDisabled.value = true
uploadDisabled.value = false
uploader.value = null
fileData.value = null
uploadRef.value?.clearFiles()
return
}
const { UploadAuth, UploadAddress, VideoId } = res.data
uploader.setUploadAuthAndAddress(uploadInfo, UploadAuth, UploadAddress, VideoId)
// console.log('🚀 ~ createUploader ~ res:', res)
ElMessage.success('文件开始上传...')
} else {
const { videoId } = uploadInfo
const [err, res] = await to(refreshUploadVideo({ video_id: videoId }))
if (err) {
pauseDisabled.value = true
resumeDisabled.value = true
uploadDisabled.value = false
uploader.value = null
fileData.value = null
uploadRef.value?.clearFiles()
return
}
const { UploadAuth, UploadAddress } = res.data
uploader.setUploadAuthAndAddress(uploadInfo, UploadAuth, UploadAddress, videoId)
// console.log('🚀 ~ createUploader ~ res:', res)
ElMessage.success('文件开始上传...')
}
},
// 文件上传成功
onUploadSucceed: (uploadInfo: { videoId: string }) => {
const { videoId } = uploadInfo
ElMessage.success('文件上传成功')
startDisabled.value = true
pauseDisabled.value = true
resumeDisabled.value = true
uploadDisabled.value = false
resumeDisabledDel.value = false
uploadRef.value?.clearFiles()
emit('uploadSuccess', videoId)
},
// 文件上传失败
onUploadFailed: (uploadInfo: any, code: any, message: string) => {
ElMessage.error(`上传失败: ${message}`)
pauseDisabled.value = true // 禁用暂停按钮
resumeDisabled.value = false // 启用继续按钮
uploadDisabled.value = true
resumeDisabledDel.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 } = uploadInfo
const [err, res] = await to(refreshUploadVideo({ video_id: videoId }))
if (err) {
ElMessage.error('凭证刷新失败,上传暂停')
pauseDisabled.value = true
resumeDisabled.value = false
resumeDisabledDel.value = false
return
}
const { uploadAuth } = res.data
uploader.value.resumeUploadWithAuth(uploadAuth)
ElMessage.success('凭证已刷新,继续上传...')
console.log('🚀 ~ createUploader ~ res:', res)
},
// 全部文件上传结束
onUploadEnd: (uploadInfo) => {
console.log('🚀 ~ onUploadEnd ~ uploadInfo:', uploadInfo)
},
})
return uploader
}
按钮相关操作(开始、暂停、恢复、删除)
点击开始按钮上传文件注意:(项目中采用自动上传模式,暂时未启用该按钮,但是需要调用到此方法)
javascript
const authUpload = () => {
if (uploader.value && fileData.value) {
uploader.value.startUpload()
startDisabled.value = true // 禁用开始按钮
pauseDisabled.value = false // 启用暂停按钮
resumeDisabled.value = true // 禁用继续按钮
uploadDisabled.value = true // 上传时不能再次点击上传
}
}
暂停上传
javascript
const pauseUpload = () => {
if (uploader.value) {
uploader.value.stopUpload()
resumeDisabled.value = false
pauseDisabled.value = true
resumeDisabledDel.value = false
ElMessage.warning('已暂停上传')
}
}
恢复上传
javascript
const resumeUpload = () => {
if (uploader.value) {
uploader.value.startUpload()
resumeDisabled.value = true
pauseDisabled.value = false
ElMessage.success('已恢复上传')
}
}
删除本地上传文件及清空上传列表及上传实例
javascript
const handleResumeDel = () => {
uploadRef.value?.clearFiles()
emit('resumeDel')
pauseDisabled.value = true // 禁用暂停按钮
resumeDisabled.value = true // 禁用继续按钮
uploadDisabled.value = false // 启用上传组件
fileData.value = null
authProgress.value = 0
uploader.value.stopUpload()
uploader.value = null
}
暴露给父组件
javascript
defineExpose({
uploadDisabled,
resumeDisabledDel,
handleResumeDel,
})
html代码
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代码
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>
未上传前效果

上传成功效果
