文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

vue 大文件分片上传(断点续传、并发上传、秒传)

2024-04-02 19:55

关注

对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率。

本文是基于 springboot + vue 实现的文件上传,本文主要介绍vue实现文件上传的步骤及代码实现,服务端(springboot)的实现步骤及实现请移步本人的另一篇文章:

springboot 大文件上传、分片上传、断点续传、秒传 

上传分步:

本人分析上传总共分为:

直接上代码

文件上传:

import md5 from 'js-md5' //引入MD5加密
import UpApi from '@/api/common.js'
import { concurrentExecution } from '@/utils/jnxh'
 

export const uploadByPieces = ({
                                 file,
                                 pieceSize = 3,
                                 concurrent = 3,
                                 success,
                                 process,
                                 error
                               }) => {
  // 如果文件传入为空直接 return 返回
  if (!file || file.length < 1) {
    return error('文件不能为空')
  }
  let fileMD5 = '' // 总文件列表
  const chunkSize = pieceSize * 1024 * 1024 // 1MB一片
  const chunkCount = Math.ceil(file.size / chunkSize) // 总片数
  const chunkList = [] // 分片列表
  let uploaded = [] // 已经上传的
  let fileType = '' // 文件类型
  // 获取md5
  
  const readFileMD5 = () => {
    // 读取视频文件的md5
    fileType = file.name.substring(file.name.lastIndexOf('.') + 1, file.name.length)
    console.log('获取文件的MD5值')
    let fileRederInstance = new FileReader()
    console.log('file', file)
    fileRederInstance.readAsBinaryString(file)
    fileRederInstance.addEventListener('load', e => {
      let fileBolb = e.target.result
      fileMD5 = md5(fileBolb)
      var index = file.name.lastIndexOf('.')
      var tp = file.name.substring(index + 1, file.name.length)
      let form = new FormData()
      form.append('filename', file.name)
      form.append('identifier', fileMD5)
      form.append('objectType', fileType)
      form.append('chunkNumber', 1)
      UpApi.uploadChunk(form).then(res => {
        if (res.skipUpload) {
          console.log('文件已被上传')
          success && success(res)
        } else {
          // 判断是否是断点续传
          if (res.uploaded && res.uploaded.length != 0) {
            uploaded = [].concat(res.uploaded)
          }
          console.log('已上传的分片:' + uploaded)
          // 判断是并发上传或顺序上传
          if (concurrent == 1 || chunkCount == 1) {
            console.log('顺序上传')
            sequentialUplode(0)
          } else {
            console.log('并发上传')
            concurrentUpload()
          }
        }
      }).catch((e) => {
        console.log('文件合并错误')
        console.log(e)
      })
    })
  }
  
  const getChunkInfo = (file, currentChunk, chunkSize) => {
    let start = currentChunk * chunkSize
    let end = Math.min(file.size, start + chunkSize)
    let chunk = file.slice(start, end)
    return {
      start,
      end,
      chunk
    }
  }
  
  const readChunkMD5 = () => {
    // 针对单个文件进行chunk上传
    for (var i = 0; i < chunkCount; i++) {
      const {
        chunk
      } = getChunkInfo(file, i, chunkSize)
 
      // 判断已经上传的分片中是否包含当前分片
      if (uploaded.indexOf(i + '') == -1) {
        uploadChunk({
          chunk,
          currentChunk: i,
          chunkCount
        })
      }
    }
  }
  
  const uploadChunk = (chunkInfo) => {
    var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100)
    console.log(sd, '进度')
    process(sd)
    console.log(chunkInfo, '分片大小')
    let inde = chunkInfo.currentChunk + 1
    if (uploaded.indexOf(inde + '') > -1) {
      const {
        chunk
      } = getChunkInfo(file, chunkInfo.currentChunk + 1, chunkSize)
      uploadChunk({
        chunk,
        currentChunk: inde,
        chunkCount
      })
    } else {
      var index = file.name.lastIndexOf('.')
      var tp = file.name.substring(index + 1, file.name.length)
      // 构建上传文件的formData
      let fetchForm = new FormData()
      fetchForm.append('identifier', fileMD5)
      fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1)
      fetchForm.append('chunkSize', chunkSize)
      fetchForm.append('currentChunkSize', chunkInfo.chunk.size)
      const chunkfile = new File([chunkInfo.chunk], file.name)
      fetchForm.append('file', chunkfile)
      // fetchForm.append('file', chunkInfo.chunk)
      fetchForm.append('filename', file.name)
      fetchForm.append('relativePath', file.name)
      fetchForm.append('totalChunks', chunkInfo.chunkCount)
      fetchForm.append('totalSize', file.size)
      fetchForm.append('objectType', tp)
      // 执行分片上传
      let config = {
        headers: {
          'Content-Type': 'application/json',
          'Accept': '*
  const sequentialUplode = (currentChunk) => {
    const {
      chunk
    } = getChunkInfo(file, currentChunk, chunkSize)
    let chunkInfo = {
      chunk,
      currentChunk,
      chunkCount
    }
    var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100)
    process(sd)
    console.log('当前上传分片:' + currentChunk)
    let inde = chunkInfo.currentChunk + 1
    if (uploaded.indexOf(inde + '') > -1) {
      console.log('分片【' + currentChunk + '】已上传')
      sequentialUplode(currentChunk + 1)
    } else {
      let uploadData = createUploadData(chunkInfo)
      let config = {
        headers: {
          'Content-Type': 'application/json',
          'Accept': '*
  const concurrentUpload = () => {
    for (var i = 0; i < chunkCount; i++) {
      chunkList.push(Number(i))
    }
    console.log('需要上传的分片列表:' + chunkList)
    concurrentExecution(chunkList, concurrent, (curItem) => {
      return new Promise((resolve, reject) => {
        const {
          chunk
        } = getChunkInfo(file, curItem, chunkSize)
        let chunkInfo = {
          chunk,
          currentChunk: curItem,
          chunkCount
        }
        var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100)
        process(sd)
        console.log('当前上传分片:' + curItem)
        let inde = chunkInfo.currentChunk + 1
        if (uploaded.indexOf(inde + '') == -1) {
          // 构建上传文件的formData
          let uploadData = createUploadData(chunkInfo)
          // 请求头
          let config = {
            headers: {
              'Content-Type': 'application/json',
              'Accept': '*
  const createUploadData = (chunkInfo) => {
    let fetchForm = new FormData()
    fetchForm.append('identifier', fileMD5)
    fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1)
    fetchForm.append('chunkSize', chunkSize)
    fetchForm.append('currentChunkSize', chunkInfo.chunk.size)
    const chunkfile = new File([chunkInfo.chunk], file.name)
    fetchForm.append('file', chunkfile)
    // fetchForm.append('file', chunkInfo.chunk)
    fetchForm.append('filename', file.name)
    fetchForm.append('relativePath', file.name)
    fetchForm.append('totalChunks', chunkInfo.chunkCount)
    fetchForm.append('totalSize', file.size)
    fetchForm.append('objectType', fileType)
    return fetchForm
  }
  readFileMD5() // 开始执行代码
}

并发控制:


export function concurrentExecution(list, limit, asyncHandle) {
  // 递归执行
  let recursion = (arr) => {
    // 执行方法 arr.shift() 取出并移除第一个数据
    return asyncHandle(arr.shift()).then(() => {
      // 数组还未迭代完,递归继续进行迭代
      if (arr.length !== 0) {
        return recursion(arr)
      } else {
        return 'finish'
      }
    })
  }
  // 创建新的并发数组
  let listCopy = [].concat(list)
  // 正在进行的所有并发异步操作
  let asyncList = []
  limit = limit > listCopy.length ? listCopy.length : limit
  console.log(limit)
  while (limit--) {
    asyncList.push(recursion(listCopy))
  }
  // 所有并发异步操作都完成后,本次并发控制迭代完成
  return Promise.all(asyncList)
}

到此这篇关于vue 大文件分片上传(断点续传、并发上传、秒传)的文章就介绍到这了,更多相关vue 大文件分片上传内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯