断点续传,分片上传

<!--
 * @Description: 上传视频组件 只封装了允许上传一个视频
 * @Author zhangyu
 * @Date 2019-09-03 15:55:42
 * @Params {}
 -->

<template>
  <div class="f-upload">
        <el-upload
          class="f-upload__video"
          :show-file-list="false"
          :action="params.action"
          :http-request="(params) => uploadFile(params)">
          <video-player
            v-if="false"
            class="video-player vjs-custom-skin"
            ref="videoPlayer"
            :playsinline="true"
            :options="playerOptions">
              <i class="el-icon-error video-delete-icon" @click="handleDelete(index-1, $event)"></i>
          </video-player>
          <i class="el-icon-plus video-uploader-icon"></i>
        </el-upload>
    <div>
      <ul class="f-upload__hit">
        注意:
        <li>请选择本地视频上传</li>
        <li>大小不能超过{{params.size}}M</li>
        <li>视频格式为MP4</li>
      </ul>
    </div>
    </div>
</template>

<script>
import { hexMd5 } from '@/utils/md5'
import { playerOptions, ConstantField } from '@/utils/constantField'
import moment from 'moment'
const DEFAULT = {
  action: '', // 必须,文件提交路由 ****暂未使用
  readonly: false, // 是否只读
  size: 100, // 最大上传大小,单位 M
  limit: 1,
  value: [] // 图片url
}

export default {
  name: 'UploadVideo',
  props: {
    config: {
      type: Object,
      required: true
    },
    name: { // 字段名
      type: String,
      required: true
    }
  },
  data () {
    return {
      playerOptions,
      params: null,
      fileForm: {
        action: '',
        date: '',
        fileMd5: '',
        index: 0,
        md5: '',
        size: '',
        total: 0
      }
    }
  },
  computed: {
    imageSrcs: function () {
      let arr = []

      this.params.value.forEach(function (val) {
        arr.push('${ConstantField.IMAGE_API}${val}')
      })
      if (arr.length > 0) {
        return arr
      } else {
        return []
      }
    },
    mimeTypes: function () {
      return ConstantField.MIME_TYPE.VIDEO.extensions
    },
    extension: function () {
      let str = null
      const val = this.mimeTypes

      if (val && val.length > 0) {
        str = ''
        for (let i = 0; i < val.length; i++) {
          str += ` .${val[i]} `
          if (i < val.length - 1) {
            str += '|'
          }
        }
      }
      return str
    }
  },
  watch: {
    config: {
      handler: function () {
        this.setParams()
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    // 将接收的config数据与默认配置合并
    setParams () {
      this.params = Object.assign({}, DEFAULT, this.config)
    },
    async beforeUpload (file) {
      const name = file.name
      const arr = name.split('.')

      if (this.mimeTypes.indexOf(arr.pop()) === -1) {
        this.$alert(`当前仅支持上传视频扩展名:${this.extension}`, '上传视频失败')
        return false
      }
      if (this.size) {
        let isLimit = file.size / 1024 / 1024 < this.params.size

        if (!isLimit) {
          this.$message.error(`上传视频大小不能超过 ${this.params.size}MB!`)
          return false
        }
      }
      this.getFileMD5(file)
      // const formVerify = new FormData()
      // const fileRes = await this.$http.attachmentApi.uploadFile(formVerify)

      // console.log('fileRes', fileRes)
    },
    async uploadFile (params) {
      const file = params.file

      await this.beforeUpload(file)
    },
    // 获取整个文件的hash
    async getFileMD5 (file) {
      const reader = new FileReader()
      const context = this

      reader.onload = async function (e) {
        const bolb = e.target.result
        const fileHash = hexMd5(bolb)

        context.fileForm.fileMd5 = fileHash
        console.log('整个文件hash ', fileHash)
        const fileRes = await context.$http.attachmentApi.isUpload(fileHash)

        console.log('dasd', fileRes)
        if (fileRes.code === 200) {
          context.fileForm.uuid = fileRes.data.uuid
          switch (fileRes.data.status) {
          case ConstantField.NOT_UPLOAD:
          case ConstantField.PART_UPLOAD:
            context.handleSuccess(file)
            break
          case ConstantField.ALREADY_UPLOAD:
            context.$message.info('该文件已经上传,请不要上传重复文件')
            break
          default:
            context.$message.error('服务器错误无法上传')
            break
          }
        }
      }
      reader.readAsBinaryString(file)
    },
    handleError (msg) {
      this.$message.error(msg || '上传视频失败,请重试')
    },
    handleDelete (index, event) {
      event.stopPropagation()
      this.$confirm('确定删除该视频?', '提示', {
        type: 'warning'
      }).then(() => {
        this.params.value.splice(index, 1)
        this.handleChange()
        this.$message.success('删除成功!')
      }).catch(() => {
        // 取消删除
      })
    },
    handleChange () {
      this.$emit('change-event', {
        name: this.name,
        value: this.params.value
      })
    },
    /**
     * 分片获取hash 上传 ConstantField.MIME_TYPE.SLICESIZE规定按5M大小拆分
     */
    async handleSuccess (file) {
      const size = file.size // 总大小
      const shardCount = Math.ceil(size / ConstantField.MIME_TYPE.SLICESIZE) // 总片数

      this.uploadSliceFile(file, 0, 1, shardCount)
      // const res = await this.$http.attachmentApi.uploadFile(form)

      // if (res.code === ConstantField.RQ_SUCCESS) {
      //   this.params.value[index] = res.data.url
      //   this.handleChange()
      //   this.$message.success('上传成功!')
      // } else {
      //   this.handleError(res.msg)
      // }
    },
    /**
     *
     * @param file
     * @param filemd5 整个文件的md5
     * @param i 文件第i个分片
     * @param type type  1为检测;2为上传
     */
    async uploadSliceFile (file, i, type, shardCount) {
      const date = moment().format('YYYYMMDD')
      // 计算每一片的起始与结束位置
      const start = i * ConstantField.MIME_TYPE.SLICESIZE
      const end = Math.min(file.size, start + ConstantField.MIME_TYPE.SLICESIZE)
      // const form = new FormData()

      // if (type === 1) {
      //   form.append('action', 'check') // 检测分片是否上传
      // } else {
      //   form.append('action', 'upload') // 直接上传分片
      //   form.append('data', file.slice(start, end)) // slice方法用于切出文件的一部分
      // }
      // form.append('filemd5', this.fileForm.fileMd5)
      this.fileForm.action = 'upload'// 直接上传 check-校验分片
      this.fileForm.date = date
      // this.fileForm.name = file.name
      this.fileForm.size = file.size
      this.fileForm.total = shardCount
      this.fileForm.index = i + 1


      // 按大小切割文件段
      const data = file.slice(start, end)
      const reader = new FileReader()

      reader.onload = async (e) => {
        const bolb = e.target.result

        const sliceHash = hexMd5(bolb)

        this.fileForm.md5 = sliceHash
        const formData = new FormData()

        // formData.append('uuid', this.fileForm.uuid)
        // formData.append('fileMd5', this.fileForm.fileMd5)
        // formData.append('action', 'upload')
        // formData.append('date', date)
        // formData.append('name', file.name)
        // formData.append('size', file.size)
        // formData.append('total', shardCount) // 总片数
        // formData.append('index', i + 1) // 当前是第几片
        // formData.append('fileUploadDTO', this.fileForm)
        formData.append('file', file)
        console.log('0628----shardCount', shardCount)
        console.log('0628----i', i)

        const res = await this.$http.attachmentApi.uploadFile(formData)

        if (res.code === ConstantField.RQ_SUCCESS) {
          // this.params.value[index] = res.data.url
          if ((i + 1) < shardCount) {
            this.uploadSliceFile(file, i + 1, 1, shardCount)
          }
        } else {
          this.handleError(res.msg)
        }
      }
      reader.readAsBinaryString(data)
    }
  },
  created () {
  }

}
</script>

<style lang="less" scoped>
@width: 400px;
@height: 400px;
@max-width: 560px;
.f-upload{
  /*最外层设置宽高 固定400px*/
  width: @width;
  height: @width;
  max-width: @max-width;
  min-height: @width;
  &__video{
    min-width: @width ;
    min-height: @width;
    border: 1px dashed #D9D9D9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    text-align: center;
    /deep/ .el-upload {
      width: 100%;
    }

    .video-uploader-icon {
      font-size: 28px;
      color: #8C939D;
      line-height: @height;
    }

    &:hover {
      border-color: #409EFF;
      color: #409EFF;

      .image-uploader-icon {
        color: #409EFF;
      }
    }

      .video-delete-icon {
        position: absolute;
        font-size: 22px;
        right: 5px;
        top: 5px;
        color: red;
        &:hover {
          color: #ff5500;
        }
      }
  }

  /*上传视频组件提示*/
  &__hit {
    font-weight: 500;
    //color: @color-hint-text;
    line-height: 20px;
    padding-top: 20px;
    padding-bottom: 10px;
    li {
      //color: @text;
      padding-bottom: 5px;
      &::before{
        content: "●";
        color: #1790FF;
        padding-right: 10px;
      }
    }
  }
}

</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值