vue3 + el-upload组件集成阿里云视频点播从本地上传至点播存储

阿里云官方文档
使用JavaScript SDK上传文件_视频点播(VOD)-阿里云帮助中心 (aliyun.com)

实现方式有多种 我使用的是 上传地址和凭证方式 及 阿里云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>

未上传前效果

上传成功效果

相关推荐
用户481178812873 小时前
求大佬解惑:高度与宽度百分比设置问题
前端
anyup3 小时前
🔥开源零配置!10 分钟上手:create-uni + uView Pro 快速搭建企业级 uni-app 项目
前端·前端框架·uni-app
帆张芳显3 小时前
智表 ZCELL 公式引擎,帮你解锁自定义函数与跨表计算的强大能力
前端·javascript
北城以北88884 小时前
Vue-- Axios 交互(一)
前端·javascript·vue.js
shelutai4 小时前
实现提供了完整的 Flutter Web 文件上传解决方案
前端·flutter
im_AMBER4 小时前
Web 开发 29
前端·学习·web
前端开发爱好者4 小时前
Vite➕ 收费了!
前端·javascript·vue.js
gplitems1234 小时前
Petslist – Pet listing WordPress Theme Free Download
linux·服务器·前端
羊羊小栈4 小时前
基于「多模态大模型 + BGE向量检索增强RAG」的新能源汽车故障诊断智能问答系统(vue+flask+AI算法)
vue.js·人工智能·算法·flask·汽车·毕业设计·大作业