前端分片下载大文件

参考博文:https://blog.csdn.net/weixin_42682011/article/details/122740407

问题描述

前端请求后端服务下载安装包等大文件时请求超时,由于文件大小不定无法通过修改超时时间解决;并且文件过大时接口请求时间长,页面无法查看请求进度,用户无法看到是否已经在下载文件。

针对该问题一开始考虑的是由后端给前端一个url地址,前端直接丢给浏览器下载,可以看到浏览器自带的下载进度,对用户相对友好;但出现了新问题,由于项目是前后端分离的项目,存在跨域情况,浏览器下载无法修改文件名(a标签的download属性只在同源有效)。后来又考虑了其他一些方法,都或多或少的存在一些问题,最终选择分片下载。

代码实现

1、调后端接口函数

export function fileDownload ({ param, bytes }) {
  return request({
    url: 'url',
    method: 'GET',
    params: param,
    responseType: 'blob',
    headers: {
      Range: bytes
    },
    timeout: 60000 // 超时时间
  })
}

fileDownload (params) {
      return new Promise(resolve => {
        // fileDownload为封装的后端接口名
        fileDownload(params).then(res => resolve(res.data))
      })
    },

2、递归函数,分片请求后端接口,提供每次请求的文件流范围,this.byteSize为每次请求的文件流大小

// 点击下载的函数(递归实现)
downloadFun (i, total, val) {
  const requestHandle = async () => {
    const params = {
      param: '后端需要的参数',
      bytes: `bytes=${i * this.byteSize}-${i + 1 >= total ? val.packageSize : (i + 1) * this.byteSize}` // 切片范围,判断是最后一次请求时切片范围是上一次请求的截止值到文件大小
    }
    const context = await this.fileDownload(params)
    val.dataStream.push(context) 
    val.progress = Number(((++i / total) * 100).toFixed(0)) // 进度的百分比展示,用于页面增加进度条
    if (i >= total) {
      // 最后一包
      this.saveFileDownload(val)
      if (type) {
            const count = this.chosenPackageList.findIndex(item => {
              return item.packageId === val.packageId
            })
            this.chosenPackageList.splice(count, 1)
            // 批量下载时限制同时下载最多3个
            if (this.chosenPackageList.length >= 3) {
              this.shardToDownload(this.chosenPackageList[2], true)
            }
          }
    } else {
      this.$nextTick(() => {
        requestHandle()
      })
    }
  }
  requestHandle()
},

3、获取到文件流数组后进行下载

// 流文件保存本地并下载
  saveFileDownload (val) {
    const saveFile = new Blob([...val.dataStream])
    FileSaver.saveAs(saveFile, '文件名')
    val.dataStream = []
    setTimeout(() => {
      val.progress = 0
    }, 3000)
  },

4、每个文件的下载处理,分片处理

  shardToDownload (item, type) {
    const total = Math.ceil(item.packageSize / this.byteSize) // 计算文件分成几片,调几次接口
    const i = 0
    item.dataStream = []
    this.$set(item, 'progress', 0)
    this.downloadFun(i, total, item, type)
  },

5、点击下载按钮实现下载,由于项目需要批量下载,这里会有个循环所有下载文件

handleDownload (file) {
  if (file) { // 判断是单文件下载还是批量下载
    this.shardToDownload(file, false)
  } else {
    this.chosenPackageList.forEach((item, index) => { // chosenPackageList为选择需要批量下载的文件
      if (index < 3) { // 批量下载时限制同时下载最多3个
        this.shardToDownload(item, true)
      }
    })
  }
},
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐