上传视频预览 Vue完整代码(获取视频第一帧)

4,373 阅读1分钟

1.问题描述:

上传附件时,默认预览展示,上传成功后,图片可正常显示

当上传为MP4等视频文件格式的文件时,fileList内其实所包含的是视频存放地址

所以el-upload预览显示默认空图加载失败

2.需求:

如果上传文件为视频时,预览展示默认为视频第一帧

点击预览,可正常播放

3.实现(完整代码):

<template>
<div>
  <el-upload
             ref="upload"
             :limit="uploadLimit"
             list-type="picture-card"
             :accept="uploadAccept"
             :before-upload="beforeUpload"
             :headers="headers"
             :on-preview="handlePreview"
             :on-success="handleSuccess"
             :on-remove="handleRemove"
             :on-exceed="handleExceed"
             :action="action"
             :file-list="fileList"
             >
    <i slot="default" class="el-icon-plus" />
  </el-upload>
  
  <!-- 预览部分 -->
  <el-dialog :visible.sync="dialogImage">
    <img width="400px" :src="dialogUrl" alt="">
  </el-dialog>
  <el-dialog :visible.sync="dialogVideo">
    <video width="600px" :src="dialogUrl" controls />
  </el-dialog>
  <div v-if="videoList[0]" style="position:absolute; right: 999999px">
    <canvas id="mycanvas" />
    <video id="upvideo" :src="videoList[0].url" style="width:320px;height:200px;" controls="controls" />
  </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        uploadLimit: 1,
        imgType: 1, // 1图片 2视频
        worksUpdateType: 3 // 1图片 2视频 3均可
      }
    },
    computed: {
      uploadAccept() {
        let text = '.png, .jpg, .mp4'
        switch (this.worksUpdateType) {
          case 1:
            text = '.png, .jpg'
            break
          case 2:
            text = '.mp4'
            break
          case 3:
            text = '.png, .jpg, .mp4'
            break
        }
        
        return text
      }
    },
    methods: {
      // 上传文件大小、格式限制
      beforeUpload(file) {
        if (file.type !== 'video/mp4') {
          this.uploadLimit = 20
          this.imgType = 1
        } else {
          const isHaveImg = this.fileList.some(item => item.suffix === 'jpg' || item.suffix === 'png')
          
          if (isHaveImg && (this.videoSrc !== '' || this.imgType === 1)) {
            this.handleExceed()
            return false
          }
          this.uploadLimit = 1
          this.imgType = 2
        }
        const testmsg = file.name.substring(file.name.lastIndexOf('.') + 1)
        const extension = testmsg === 'mp4'
        const isJPG = file.type === 'image/jpg' || file.type === 'image/jpeg' || file.type === 'image/png'
        const isLt5M = file.size / 1024 / 1024 < 5
        const isLt2M = file.size / 1024 / 1024 < 100
        if (isJPG) {
          if (!isLt5M) {
            this.$message.error('上传图片大小不能超过 5MB!')
          }
        } else if (extension) {
          if (!isLt2M) {
            this.$message.error('上传视频大小不能超过 100MB!')
          }
        }
        
        const n = this.worksUpdateType
        if (n === 1 && !isJPG) {
          this.$message.error('上传图片只能是 JPG、PNG格式!')
          return isJPG && isLt5M
        } else if (n === 2 && !extension) {
          this.$message.error('上传视频只能是 MP4格式!')
          return extension && isLt2M
        } else if (n === 3 && !isJPG && !extension) {
          this.$message.error('上传文件只能是 JPG、PNG、MP4格式!')
          return (isJPG && isLt5M) || (extension && isLt2M)
        }
      },
      // 上传成功图片/视频区分处理(如果为视频截取第一帧)
      handleSuccess(res, file, fileList) {
        let newFile = null
        if (res.success) {
          newFile = res.data.mapList[0]
          
          if (newFile.suffix === 'mp4') {
            this.fileList = []
            this.fileList.push(newFile)
            this.videoList = [newFile]
            this.videoSrc = newFile.url
            
            // 需要DOM更新后操作,否则会报错
            this.$nextTick(() => {
              this.findvideocover(newFile.url)
            })
          } else {
            this.fileList.push(newFile)
            this.form.imgPath = newFile.url
          }
        }
      },
      // 截取视频第一帧作为播放前默认图片
      async findvideocover(url, file) {
        const video = document.querySelector('#upvideo') // 获取视频对象
        
        video.src = url // url地址 url跟 视频流是一样的
        var canvas = document.getElementById('mycanvas') // 获取 canvas 对象
        const ctx = canvas.getContext('2d') // 绘制2d
        video.crossOrigin = 'anonymous' // 解决跨域问题,也就是提示污染资源无法转换视频
        video.currentTime = 1 // 第一帧
        video.oncanplay = () => {
          canvas.width = video.clientWidth // 获取视频宽度
          canvas.height = video.clientHeight // 获取视频高度
          // 利用canvas对象方法绘图
          ctx.drawImage(video, 0, 0, video.clientWidth, video.clientHeight)
          // 转换成base64形式
          this.imgsrc = canvas.toDataURL('image/png') // 截取后的视频封面
          // 将base64转化成图片上传获取地址url
          const res = this.dataURLtoFileFun(this.imgsrc, 'file.jpg')
          this.getVideoImgUrl(res)
        }
      },
      dataURLtoFileFun(dataurl, filename) {
        // 将base64转换为文件,dataurl为base64字符串,filename为文件名(必须带后缀名,如.jpg,.png)
        const arr = dataurl.split(',')
        const mime = arr[0].match(/:(.*?);/)[1]
        const bstr = atob(arr[1])
        let n = bstr.length
        const u8arr = new Uint8Array(n)
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n)
        }
        
        return new File([u8arr], filename, { type: mime })
      },
      
      // 将截取到第一帧的图片上传拿到url地址
      async getVideoImgUrl(res) {
        const formData = new FormData()
        formData.append('file', res)
        formData.append('size', 100)
        formData.append('unit', 'M')
        const { data } = await worksList.uploadFile(formData)
        this.fileList.push(data.mapList[0])
        this.form.imgPath = data.mapList[0].url
      },
      handleRemove(file, fileList) {
        if (file.suffix === 'mp4') {
          this.uploadLimit = 20
          this.imgType = 1
        }
        
        if (file.status === 'success') this.fileList = fileList
        this.dialogUrl = null
      },
      handlePreview(file) {
        if (this.videoSrc !== '') {
          this.dialogVideo = true
          this.dialogUrl = this.videoSrc
        } else {
          this.dialogUrl = file.url
          this.dialogImage = true
        }
      },
    }
  }
</script>