首页 > 其他分享 >js分片上传&断点续传

js分片上传&断点续传

时间:2023-10-19 13:33:07浏览次数:449  
标签:断点续传 chunkSize 文件 js uploadId file 分片 上传

原理

js将大文件分成多分,全部上传成功之后,调用合并接口合成文件。如果传输中断,下次上传的时候过滤掉已经上传成功的分片,将剩余的分片上传,成功之后合并文件。

前置条件

  • 获取uoloadId接口(用于标记分片)
  • 分片上传接口
  • 合成文件接口(后端自动合成则不需要)
  • 查询已上传的分片列表接口(断点续传)

文件分片

h5中使用slice就可以分割文件

// 文件分片大小
const maxFileSize = 100 * 1024
// 分片数量
const nums = Math.ceil(file.size / maxFileSize)
for (let i = 1; i <= nums; i++) {
  // 文件上传进度
  let progress = 0
  // 分割文件  上传
  upload(file.slice((i - 1) * maxFileSize, i * maxFileSize), uploadId).then(() => {
    progress++
    if (progress >= nums) { // 完全上传成功
      // 合并文件
      merge(uploadId).then(res => {
        // 返回文件地址
        console.log(res)
      })
    }
  })
}

断点续传

在上传分片的时候我们将文件和uploadId存入本地,下次上传时用uploadId从接口获取已经上传成功的分片(这里没有接口,我们存入本地),将已上传的分片过滤,上传剩余。

// 使用文件的属性拼成一个唯一id,作为localStorage的key
const id = file.lastModified + file.size + file.type + file.name
// 取uploadId 和 已上传成功的分片
let { ids = [], uploadId } = Storage.getObj(id)
// 进度
let progress = ids.length
// 分片数量
const nums = Math.ceil(file.size / maxFileSize)
for (let i = 1; i <= nums; i++) {
  // 如果当前分片已经上传成功 跳过本次上传
  if (ids.includes(i)) continue
  // 分割文件  上传
  upload(file.slice((i - 1) * maxFileSize, i * maxFileSize), uploadId).then(() => {
    progress++
    if (progress >= nums) { // 完全上传成功
      // 合并文件
      merge(uploadId).then(res => {
        // 返回文件地址
        console.log(res)
      })
      // 合并请求之后清除缓存
      Storage.remove(id)
    } else {
      ids.push(i)
      // 已上传的分片存入本地
      Storage.setObj(id, {
        ids,
        uploadId
      })  
    }
  })
}

完整代码

class Uploader {
  // 分片大小(kb)
  chunkSize = 0

  uuid = ''

  constructor({ chunkSize, upload, mergeFile, getUploadId }) {
    this.uuid = uuidv4()
    this.chunkSize = chunkSize * 1024
    this.upload = upload // 上传文件
    this.mergeFile = mergeFile // 合并文件
    this.getUploadId = getUploadId // 获取uploadId
  }

  async start(file, onProgress) {
    // 判断是否使用分片上传
    if (this.chunkSize > 0 && file.size > this.chunkSize) {
      // uuid拼接文件各个属性, 作为localStorage的key
      const key =
        this.uuid + file.lastModified + file.size + file.type + file.name
      // 去缓存中已上传的分片和uploadId
      let { chunks = [], uploadId } = Storage.getObj(key)
      // 从接口去uploadId
      if (!uploadId) {
        uploadId = await this.getUploadId()
      }
      return new Promise(resolve => {
        const chunkNums = Math.ceil(file.size / chunkSize)
        // 进度
        let progress = 0

        for (let i = 0; i < chunkNums; i++) {
          // 缓存中存在已上传的分片 跳过上传
          if (chunks.includes(e => e.index === i)) continue
          this.upload(
            file.slice(i * chunkSize, (i + 1) * chunkSize),
            uploadId,
            i
          ).then(async res => {
            chunks.push({
              index: i,
              chunk: file.slice(i * chunkSize, (i + 1) * chunkSize),
              uploadData: res.data
            })

            progress++
            onProgress(progress / chunkNums)
            if (progress >= chunkNums) {
              // 全部分片上传成功
              Storage.remove(key)
              if (this.mergeFile) {
                resolve(await this.mergeFile(chunks, uploadId))
              }
              resolve(res)
            } else {
              Storage.setObj(key, {
                chunks,
                uploadId
              })
            }
          })
        }
      })
    } else {
      return this.upload(file)
    }
  }
}

// 实例化
const uploader = new Uploader({
  chunkSize: 100,
  getUploadId: GetUploadId,
  upload: (file, uploadId, index) =>
    UploadFile(file, uploadId, index && index + 1),
  mergeFile: (chunks, uploadId) =>
    MergeFile(
      uploadId,
      chunks.map(e => ({ partNumber: e.index + 1 }))
    )
})

// 上传图片调用
async change(e) {
    console.log(e.target.files)
    if (!e.target.files.length) return
    const { data } = await uploader.start(
      e.target.files[0],
      e => (this.progress = e)
    )
    console.log(readUrl + data.data)
}

大文件下载

同理,当需要下载大文件时,可以将文件拆分(后端支持),下载完成后在前端拼接成一整个文件。

const chunks = []
const nums = Math.ceil(file.size / maxFileSize)
for (let i = 1; i <= nums; i++) {
  chunks.push(file.slice((i - 1) * maxFileSize, i * maxFileSize))
}
const blob = new Blob(chunks)
   

参考文章:http://blog.ncmem.com/wordpress/2023/10/19/js%e5%88%86%e7%89%87%e4%b8%8a%e4%bc%a0%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0-2/

欢迎入群一起讨论

 

 

标签:断点续传,chunkSize,文件,js,uploadId,file,分片,上传
From: https://www.cnblogs.com/songsu/p/17774496.html

相关文章

  • linux centos7 部署 nodejs 的 express
      链接:https://www.youtube.com/watch?v=oGbLL1_0q64 yuminstallgcc-c++openssl-develmake-ycd/tmpcurl-sLhttps://rpm.nodesource.com/setup|bash-yuminstall-ynodejsnpminstall-gexpress-generatoruseraddusernamepasswdusernamemkdirfirst_project......
  • 关于 js ajax readyState 的阐述
    为了弄清楚ajaxreadyState的具体意义,做了如下测试:index.html1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8"/>5<metaname="viewport"content="width=device-width,init......
  • xStream完美转换XML、JSON
    xStream框架xStream可以轻易的将Java对象和xml文档相互转换,而且可以修改某个特定的属性和节点名称,而且也支持json的转换;前面有介绍过json-lib这个框架,在线博文:以及Jackson这个框架,在线博文:它们都完美支持JSON,但是对xml的支持还不是很好。一定程度上限制了对Java对象的描述,不能让xml......
  • js实现文件切片上传,断点续传
    断点续传:顾名思义,继续上次断开的点,继续上传。思路整理:拿到文件,对文件进行fingerprint=md5(file),得到文件指纹。将指纹保存服务器。切割文件,分段上传,每次上传一段。服务器根据指纹进行索引判断文件上传进度,直到文件的全部片段上传完毕。以下文字没有完整的代码,只有基础......
  • html+css3+anime.js实现线条来回滑动且渐隐动画
    效果: 代码:<!DOCTYPEhtml><html><head><metahttp-equiv="Content-Type"content="text/html;charset=UTF-8"/><metaname="viewport"content="width=device-width,initial-sc......
  • vue2和vue3导出页面为PDF格式:jspdf和html2canvas
    一、vue2导出PDF使用步骤1、安装html2canvas,将页面html转换成图片npminstall--savehtml2canvas卸载:npmuninstallhtml2canvas指定版本安装:[email protected]、安装jspdf,将图片生成pdfnpminstalljspdf--save3、定义全局函数在指......
  • jeecg中jsp页面合计问题
    问题:在一个tbody下,如何数量和价格,合计出单项的金额,同时在最下面合计出总金额 页面的jsp代码如下: <tableborder="0"cellpadding="2"cellspacing="0"id="bgLwg_table"><trbgcolor="#E6E6E6"><tdalign="center&......
  • html+css3+anime.js实现文字故障动画
    记录一个很酷的动画,效果如图: 是基于html+css3+anime.js实现的,看了眼代码,其实是默认文字的div中定位了几个相同的文字块,利用clip-path这个属性去裁剪展示其中的一部分,用于展示故障的效果,动画则是使用anime.js去循环改变X与Y轴的位置,同时调整绘制的颜色,demo代码如下:<!DOCTYPEh......
  • javascript之分片上传,断点续传的实际项目实现详解
    首先,我们需要了解什么是分片上传和断点续传。分片上传是将大文件分成多个小块进行上传,每个小块可以独立上传,从而提高上传速度和稳定性。而断点续传是指在上传大文件时,当上传过程中因断网或其他原因中断,再次上传时可以不用重头开始,而是从中断的地方继续上传。接下来是分片上传和......
  • laravel:异常时返回json(10.27.0)
    一,相关文档:https://learnku.com/docs/laravel/10.x/errors/14857#87364d二,php代码:1,app\exceptions\Handler.php增加以下一段:1234567891011//重写renderpublicfunctionrender($request,Throwable$e){    if(env('APP_DEBUG')){  ......