首页 > 其他分享 >JS上传大文件的三种解决方案

JS上传大文件的三种解决方案

时间:2023-02-20 11:11:47浏览次数:47  
标签:文件 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的keyconst 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)

 

控件源码下载:

asp.net源码下载jsp-springboot源码下载jsp-eclipse源码下载jsp-myeclipse源码下载php源码下载csharp-winform源码下载vue-cli源码下载c++源码下载

详细配置信息及思路


标签:文件,chunkSize,解决方案,JS,uploadId,file,分片,上传
From: https://www.cnblogs.com/zyzzz/p/17136625.html

相关文章

  • 实现方法继承 js 230220
    需求让子对象可用父方法让子构造指向父构造存在的问题如果给子构造的原型添加独有方法会影响到父构造的原型与实际需求的逻辑不符合问题复现代码:functionAnimal(){thi......
  • springboot实现文件上传下载
    1.用IDEA创建名叫springboot-file的SpringBoot项目,并将Packagename改为com.example.springboot,导入SpringWeb和thymeleaf依赖。(如果创建过程中遇到了问题,可以看我写的文......
  • 根据两点经纬度计算两点间距离 js
    getDistance(lat1,lng1,lat2,lng2){letradLat1=lat1*Math.PI/180.0;letradLat2=lat2*Math.PI/180......
  • ABAP-文件的上传下载以及存表
    1REPORTzfile.23DATA:lv_urlTYPEstring.45CALLMETHODcl_gui_frontend_services=>execute6EXPORTING7*document='https://123.......
  • 某保险影像上传接口浅记
    好久没写文章了,目前的工作基本不涉及到逆向,都快忘了还有这项技能了,刚好,最近遇到一个加密参数问题,浅记一下接口分析选择图片后上传图中请求为我们需要分析的接口以及加......
  • struts2 swfupload 上传文件 flash多文件上传
    这个是转的,直接看demo吧上传不上去的原因的是没有新建一个文件夹upload可以用的!​​swfupload.rar​​(3.4MB)下载次数:111......
  • JSR303字段校验规则(笔记)
    ###直接上示例代码引入依赖(如果项目中没有的话)<dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId>......
  • NodeJS:使用 superagent 和 cheerio 爬取网页
    安装依赖:npminstallsuperagentcheerio--save同步代码:constsuperagent=require('superagent')constcheerio=require('cheerio')constfs=require('fs')f......
  • JS 异步
    回调函数ES5:使用回调函数处理异步执行的结果setTimeout(()=>{console.log('1')setTimeout(()=>{console.log('2')setTimeout(()=>{console......
  • js得到当前窗口内的宽度和高度
    vars="";s+="\r\n网页可见区域宽:"+document.body.clientWidth;s+="\r\n网页可见区域高:"+document.body.clientHeight;s+="\r\n网页可见区域宽:"+document.......