返回顶部
首页 > 资讯 > 前端开发 > JavaScript >React+Node实现大文件分片上传、断点续传秒传思路
  • 116
分享到

React+Node实现大文件分片上传、断点续传秒传思路

2024-04-02 19:04:59 116人浏览 安东尼
摘要

目录1、整体思路2、实现步骤2.1 文件切片加密2.2 查询上传文件状态2.3 秒传2.4 上传分片、断点续传2.5 合成分片还原完整文件3、总结4、后续扩展与思考5、源码1、整体思

1、整体思路

  • 将文件切成多个小的文件;
  • 将切片并行上传;
  • 所有切片上传完成后,服务器端进行切片合成;
  • 当分片上传失败,可以在重新上传时进行判断,只上传上次失败的部分实现断点续传;
  • 当切片合成为完整的文件,通知客户端上传成功;
  • 已经传到服务器的完整文件,则不需要重新上传到服务器,实现秒传功能;

2、实现步骤

2.1 文件切片加密

利用MD5 , MD5 是文件的唯一标识,可以利用文件的 MD5 查询文件的上传状态;

读取进度条进度,生成MD5:

实现结果:

实现代码如下:

const md5File = (file) => {
    return new Promise((resolve, reject) => {
      // 文件截取
      let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.WEBkitSlice,
        chunkSize = file?.size / 100,
        chunks = 100,
        currentChunk = 0,
        spark = new SparkMD5.ArrayBuffer(),
        fileReader = new FileReader();

      fileReader.onload = function (e) {
        console.log('read chunk nr', currentChunk + 1, 'of', chunks);
        spark.append(e.target.result);
        currentChunk += 1;

        if (currentChunk < chunks) {
          loadNext();
        } else {
          let result = spark.end()
          resolve(result)
        }
      };

      fileReader.onerror = function () {
        message.error('文件读取错误')
      };

      const loadNext = () => {
        const start = currentChunk * chunkSize,
          end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

        // 文件切片
        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        // 检查进度条
        dispatch({ type: 'check', checkPercent: currentChunk + 1 })
      }

      loadNext();
    })
  }

2.2 查询上传文件状态

利用当前md5去查询服务器创建的md5文件夹是否存在,如果存在则返回该目录下的所有分片;

前端只需要拿MD5和文件名去请求后端,这里就不在列出来;
node端代码逻辑:

app.get('/check/file', (req, resp) => {
  let query = req.query
  let fileName = query.fileName
  let fileMd5Value = query.fileMd5Value
  // 获取文件Chunk列表
  getChunkList(
      path.join(uploadDir, fileName),
      path.join(uploadDir, fileMd5Value),
      data => {
          resp.send(data)
      }
  )
})

// 获取文件Chunk列表
async function getChunkList(filePath, folderPath, callback) {
  let isFileExit = await isExist(filePath)
  let result = {}
  // 如果文件已在存在, 不用再继续上传, 真接秒传
  if (isFileExit) {
      result = {
          stat: 1,
          file: {
              isExist: true,
              name: filePath
          },
          desc: 'file is exist'
      }
  } else {
      let isFolderExist = await isExist(folderPath)
      // 如果文件夹(md5值后的文件)存在, 就获取已经上传的块
      let fileList = []
      if (isFolderExist) {
          fileList = await listDir(folderPath)
      }
      result = {
          stat: 1,
          chunkList: fileList,
          desc: 'folder list'
      }
  }
  callback(result)
}

2.3 秒传

如果上传的当前文件已经存在服务器目录,则秒传;

服务器端代码已给出,前端根据返回的接口做判断;

if (data?.file) {
  message.success('文件已秒传')
  return
}

实现效果:

2.4 上传分片、断点续传

检查本地切片和服务器对应的切片,如果没有当前切片则上传,实现断点续传;
同步并发上传所有的切片,维护上传进度条状态;
前端代码:


  async function checkAndUploadChunk(file, fileMd5Value, chunkList) {
    let chunks = Math.ceil(file.size / chunkSize)
    const requestList = []
    for (let i = 0; i < chunks; i++) {
      let exit = chunkList.indexOf(i + "") > -1
      // 如果不存在,则上传
      if (!exit) {
        requestList.push(upload({ i, file, fileMd5Value, chunks }))
      }
    }

    // 并发上传
    if (requestList?.length) {
      await Promise.all(requestList)
    }
  }

    // 上传chunk
  function upload({ i, file, fileMd5Value, chunks }) {
    current = 0
    //构造一个表单,FORMData是HTML5新增的
    let end = (i + 1) * chunkSize >= file.size ? file.size : (i + 1) * chunkSize
    let form = new FormData()
    form.append("data", file.slice(i * chunkSize, end)) //file对象的slice方法用于切出文件的一部分
    form.append("total", chunks) //总片数
    form.append("index", i) //当前是第几片     
    form.append("fileMd5Value", fileMd5Value)
    return axiOS({
      method: 'post',
      url: BaseUrl + "/upload",
      data: form
    }).then(({ data }) => {
      if (data.stat) {
        current = current + 1
        const uploadPercent = Math.ceil((current / chunks) * 100)
        dispatch({ type: 'upload', uploadPercent })
      }
    })
  }

Node端代码:

app.all('/upload', (req, resp) => {
  const form = new formidable.IncomingForm({
      uploadDir: 'nodeServer/tmp'
  })
  form.parse(req, function(err, fields, file) {
      let index = fields.index
      let fileMd5Value = fields.fileMd5Value
      let folder = path.resolve(__dirname, 'nodeServer/uploads', fileMd5Value)
      folderIsExit(folder).then(val => {
          let destFile = path.resolve(folder, fields.index)
          copyFile(file.data.path, destFile).then(
              successLog => {
                  resp.send({
                      stat: 1,
                      desc: index
                  })
              },
              errorLog => {
                  resp.send({
                      stat: 0,
                      desc: 'Error'
                  })
              }
          )
      })
  })

实现效果:

存储形式:

2.5 合成分片还原完整文件

当所有的分片上传完成,前端通知服务器端分片上传完成,准备合成;

前端代码:

  
  function notifyServer(file, fileMd5Value) {
    let url = BaseUrl + '/merge?md5=' + fileMd5Value + "&fileName=" + file.name + "&size=" + file.size
    axios.get(url).then(({ data }) => {
      if (data.stat) {
        message.success('上传成功')
      } else {
        message.error('上传失败')
      }
    })
  }

Node端代码:

// 合成
app.all('/merge', (req, resp) => {
  let query = req.query
  let md5 = query.md5
  let fileName = query.fileName
  console.log(md5, fileName)
  mergeFiles(path.join(uploadDir, md5), uploadDir, fileName)
  resp.send({
      stat: 1
  })
})


// 合并文件
async function mergeFiles(srcDir, targetDir, newFileName) {
  let fileArr = await listDir(srcDir)
  fileArr.sort((x,y) => {
      return x-y;
  })
  // 把文件名加上文件夹的前缀
  for (let i = 0; i < fileArr.length; i++) {
      fileArr[i] = srcDir + '/' + fileArr[i]
  }
  concat(fileArr, path.join(targetDir, newFileName), () => {
      console.log('合成成功!')
  })
}

请求实现:

合成文件效果:

3、总结

将文件切片,并发上传切片,切片合成完整文件,实现分片上传;
使用MD5标识文件夹,得到唯一标识;
分片上传前通过文件 MD5 查询已上传切片列表,上传时只上传未上传过的切片,实现断点续传;
检查当前上传文件,如果已存在服务器,则不需要再次上传,实现秒传;

4、后续扩展与思考

使用时间切片计算hash

当文件过大时需要计算很久的hash,页面不能做其他的操作,所以考虑使用React-Fiber的架构理念,利用浏览器空闲时间去计算hash。考虑使用window.requestIdleCallback()函数;

请求并发控制

假如一个文件过大,就会切割成许多的碎片,一次性发几百个请求,这显然是不行的;所以要考虑请求并发数控制;

5、源码

地址:https://GitHub.com/linhexs/file-upload.git

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

--结束END--

本文标题: React+Node实现大文件分片上传、断点续传秒传思路

本文链接: https://lsjlt.com/news/138564.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • React+Node实现大文件分片上传、断点续传秒传思路
    目录1、整体思路2、实现步骤2.1 文件切片加密2.2 查询上传文件状态2.3 秒传2.4 上传分片、断点续传2.5 合成分片还原完整文件3、总结4、后续扩展与思考5、源码1、整体思...
    99+
    2024-04-02
  • springboot大文件上传、分片上传、断点续传、秒传的实现
    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达...
    99+
    2024-04-02
  • vue 大文件分片上传(断点续传、并发上传、秒传)
    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达...
    99+
    2024-04-02
  • 如何使用大文件上传:秒传、断点续传、分片上传方法
    本篇内容介绍了“如何使用大文件上传:秒传、断点续传、分片上传方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!秒传1、什么是秒传通俗的说,你...
    99+
    2023-06-15
  • Vue+Node实现大文件上传和断点续传
    目录源代码Blob.slice切片初始化文件内容FormData.append()大文件上传断点续传代码创建切片源码worker 线程通讯的逻辑断点续传秒传源代码 断点续传、分片上传...
    99+
    2024-04-02
  • Vue+Node怎么实现大文件上传和断点续传
    本篇内容介绍了“Vue+Node怎么实现大文件上传和断点续传”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!源代码断点续传、分片上传、秒传、重...
    99+
    2023-06-30
  • vue怎么实现大文件分片上传与断点续传送
    本文小编为大家详细介绍“vue怎么实现大文件分片上传与断点续传送”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue怎么实现大文件分片上传与断点续传送”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。问题:前段时间...
    99+
    2023-07-02
  • vue实现大文件分片上传与断点续传到七牛云
    问题: 前段时间做视频上传业务,通过网页上传视频到服务器。 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题: 1、文件过大,超出服务端的请求...
    99+
    2024-04-02
  • Vue实现大文件分片上传,包括断点续传以及上传进度条
    首先解释一下什么是分片上传         分片上传就是把一个大的文件分成若干块,一块一块的传输。这样做的好处可以减少重新上传的开销。比如:如果我们上传的文件是一个很大的文件,那么上传的时间应该会比较久,再加上网络不稳定各种因素的影响,很容...
    99+
    2023-09-27
    vue.js 前端 javascript
  • Vue 大文件上传和断点续传的实现
    目录文件上传的 2 套方案基于文件流(form-data)客户端把文件转换为 base64大文件上传获取到文件对象并转成 ArrayBuffer 对象创建切片发送请求所有切片发送成功...
    99+
    2024-04-02
  • Node.js实现分片上传断点续传示例详解
    目录正文文件的分片与合并并发控制使代码可复用服务端接口实现正文 大文件上传会消耗大量的时间,而且中途有可能上传失败。这时我们需要前端和后端配合来解决这个问题。 解决步骤: 文件分片,...
    99+
    2024-04-02
  • Vue在大文件上传和断点续传的实现方法
    本篇内容主要讲解“Vue在大文件上传和断点续传的实现方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue在大文件上传和断点续传的实现方法”吧!文件上传的 2 套方案基于文件流(form-da...
    99+
    2023-06-20
  • JavaScript利用切片实现大文件断点续传
    目录什么是断点续传实现思路需要后端提供的api获取已经上传的切片上传切片合并切片前端代码细节实现HASH值的获取方法切片处理总体html结构使用axios发送请求整体逻辑和代码实现效...
    99+
    2024-04-02
  • java如何实现文件切片上传服务器+断点续传
    这篇文章主要为大家展示了“java如何实现文件切片上传服务器+断点续传”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“java如何实现文件切片上传服务器+断点续传”这篇文章吧。1.定义一个实体类用...
    99+
    2023-06-22
  • vue+element+oss实现前端分片上传和断点续传
    纯前端实现: 切片上传 断点续传 。断点续传需要在切上上传的基础上实现 前端之前上传OSS,无需后端提供接口。先上完整代码,直接复制,将new OSS里的参数修改成自己公司OSS相关...
    99+
    2024-04-02
  • java实现文件切片上传百度云+断点续传的方法
    前言: 本文代码通过dubbo进行远程调用的接口,如果不使用dubbo,直接将service放到你的service,并稍作修改,redis替换成自己封装的工具即可。下方代码有点多,但...
    99+
    2024-04-02
  • js自己实现一个大文件切片上传+断点续传的示例代码
    目录首先我们来分析一下需求一、 格式校验二、 文件切片三、 断点续传 + 秒传 + 上传进度PM:喂,那个切图仔,我这里有个100G的视频要上传,你帮我做一个上传后台,下班前给我哦,...
    99+
    2024-04-02
  • SpringBoot超大文件上传实现秒传功能
    目录1.分片上传1.1 什么是分片上传1.2 分片上传的场景2.断点续传2.1 什么是断点续传2.2 应用场景2.3 实现断点续传的核心逻辑2.4 实现流程步骤3.分片上传/断点上传...
    99+
    2022-12-10
    Spring Boot秒传文件 Spring Boot超大文件上传 springboot大文件上传
  • Html5大文件断点续传的实现方法
    本篇内容介绍了“Html5大文件断点续传的实现方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • java实现文件的断点续传
    所谓文件的断点续传,就是一个线程传输文件,另一个线程控制传输标识,以达到暂停文件效果、恢复文件上传的效果。 本demo使用最基本的线程之间的通信来实现一个简单的断点续传。 packa...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作