【js】【前端】大文件视频音频分片上传

背景

对于大文件 视频,音频的上传,可以采用分片上传,其中前端代码如下

其中 browser-md5-file 需要自行install

import {
    
     checkFile, uploadFile, uploadFileSmall } from 'services/upload'
import BMF from 'browser-md5-file'
import Tool from '../utils/tool'
import {
    
     reject } from 'lodash'

const bmf = new BMF()

/**
 * 文件唯一标识符
 * @param file
 * @returns {string}
 */
const getFileUniqueKey = file => {
    
    
  return new Promise(resolve => {
    
    
    let fileUniqueKey = null
    bmf.md5(file, (err, md5) => {
    
    
      console.log('err:', err)
      console.log('md5 string:', md5)
      const key10 = parseInt(md5, 16)
      fileUniqueKey = Tool._10to62(key10)
      resolve(fileUniqueKey)
    })
  })
}

/* 获取视频或者音频时长 */
const getDuration = file => {
    
    
  var binaryData = []
  // 传入file中raw
  binaryData.push(file)
  // 获取视频或者音频时长
  var fileurl = URL.createObjectURL(new Blob(binaryData))
  // 经测试,发现audio也可获取视频的时长
  var audioElement = new Audio(fileurl)
  audioElement.addEventListener('loadedmetadata', function(_event) {
    
    
    // audioElement.duration就是视频时长
    file.duration = audioElement.duration
  })
}
const getFileShard = (shardIndex, shardSize, file) => {
    
    
  const start = (shardIndex - 1) * shardSize // 当前分片起始位置
  const end = Math.min(file.size, start + shardSize) // 当前分片结束位置
  const fileShard = file.slice(start, end) // 从文件中截取当前的分片数据
  return fileShard
}
const upload = (param, file) => {
    
    
  return new Promise(resolve => {
    
    
    const imageUrls = []
    const shardIndex = param.shardIndex
    const shardTotal = param.shardTotal
    const shardSize = param.shardSize
    const fileShard = getFileShard(shardIndex, shardSize, file) // 从文件中截取当前的分片数据
    // 将文件转唯base64
    const fileReader = new FileReader()
    console.log('进度', parseInt((shardIndex - 1) * 100) / shardTotal)

    fileReader.onload = function(e) {
    
    
      const base64 = e.target.result
      param.shard = base64

      uploadFile(param).then(res => {
    
    
        console.log('uploadFile', param)
        console.log('进度', parseInt(shardIndex * 100) / shardTotal)

        imageUrls.push(res)
        if (shardIndex < shardTotal) {
    
    
          // 上传下一个分片
          param.shardIndex = param.shardIndex + 1
          upload(param, file)
          console.log('分片 ', shardIndex)
        } else {
    
    
        }
        resolve(imageUrls[0])
      })
    }
    fileReader.readAsDataURL(fileShard)
  })
}
/**
 * 检查文件状态,是否已上传过?传到第几个分片?
 */

// insertFn 非必传
const check = (param, file, insertFn) => {
    
    
  return new Promise((resolve, reject) => {
    
    
    checkFile(param.key).then(response => {
    
    
      console.log('response', response)

      if (response.message == '') {
    
    
        param.shardIndex = 1
        console.log('没有找到文件记录,从分片1开始上传')
        upload(param, file).then(res => {
    
    
          console.log('upload上传后返回的数据', res)
          // if (res.message.path && insertFn) insertFn(res.message.path)

          if (res.message.path) {
    
    
            if (insertFn) insertFn(res.message.path)
            resolve(res.message)
          }
        })
      } else if (response.message.shardIndex === response.message.shardTotal) {
    
    
        // 已上传分片 = 分片总数,说明已全部上传完,不需要再上传
        console.log(
          '文件极速秒传成功!',
          response.shardIndex,
          response.shardTotal
        )
        response.message.duration = param.duration
        console.log('upload上传后返回的数据', response)
        if (response.message.path) {
    
    
          if (insertFn) insertFn(response.message.path)
          resolve(response.message)
        }
      } else {
    
    
        param.shardIndex = response.shardIndex + 1
        console.log('找到文件记录,从分片' + param.shardIndex + '开始上传')
        upload(param, file)
      }
    })
  })
}

/**
 * 图片上传/音视频上传 调用uploadFunc() 即可
 *
 * @param file
 * @param suffixs Array 多媒体类型 ['mp3']
 * @param usage String 用途,必传值general, avatar, picture, voice, pdf, txt, video
 * @function insertFn 非必须 富文本多媒体customUpload需要, 其他非必须
 *
 */
const uploadFunc = (file, suffixs, usage, insertFn) => {
    
    
  // const suffixs = ['jpg', 'jpeg', 'png']
  // const usage = 'picture' // 注意这里要注明用途,必传值general, avatar, picture, voice, pdf, txt, video
  var duration = getDuration(file)
  let filename = file.name
  const suffix = filename
    .substring(filename.lastIndexOf('.') + 1, filename.length)
    .toLowerCase()
  let validateSuffix = false
  for (let i = 0; i < suffixs.length; i++) {
    
    
    if (suffixs[i].toLowerCase() === suffix) {
    
    
      validateSuffix = true
      break
    }
  }
  if (!validateSuffix) {
    
    
    console.log('文件格式不正确!只支持上传:' + suffixs.join(','))
    return
  }
  return new Promise((resolve, reject) => {
    
    
    getFileUniqueKey(file).then(res => {
    
    
      let key = res
      // 文件分片
      const shardSize = 5 * 1024 * 1024 // 以5MB为一个分片
      const shardIndex = 1 // 分片索引,1表示第1个分片
      const size = file.size
      const shardTotal = Math.ceil(size / shardSize) // 总片数
      const param = {
    
    
        shard: '',
        shardIndex,
        shardSize,
        shardTotal,
        usage,
        filename,
        suffix,
        size,
        key,
        duration:file.duration,
      }
      if (insertFn) {
    
    
        check(param, file, insertFn)
      } else {
    
    
        check(param, file).then(data => {
    
    
          if (data) resolve(data)
        })
      }
    })
  })
}

tool.js

  /**
   * 10进制转62进制
   * @param number
   * @returns {string}
   * @private
   */
  _10to62: function (number) {
    
    
    let chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
    let radix = chars.length;
    let arr = [];
    do {
    
    
      let mod = number % radix;
      number = (number - mod) / radix;
      arr.unshift(chars[mod]);
    } while (number);
    return arr.join('');
  }

猜你喜欢

转载自blog.csdn.net/qq_45481971/article/details/130577376