使用web Worker实现前端导出大数据excel表格

需求背景:业务后台下载数据表格是调用后端接口直接下载的,但是数据量超过十万级别后,接口总是出现崩溃的情况,后端经过排查并没有找到原因,所以我就给出前端下载的方案

技术方案:由前端分页查询接口,最后汇总数据下载excel,同时由于数据量大,下载过程会比较漫长,下载过程中不能影响用户进行别的操作,所以使用 Web Worker 开启一个独立的线程进行数据的获取

我是在vue项目中调用的,可根据自己项目技术栈进行修改,好了着急改bug,我直接上代码:

首先,确定一下调用方式

js 复制代码
// 这是需要下载excel的页面.vue

// 定义表头信息
const HEAD_INFO = {
  mid: '客户ID',
  nickName: '昵称',
  livePeriod: '直播期数'
  ...
}

// 点击按钮,调用exportExcel
async exportExcel () {
  this.exportPageLoading = true
  let flowPageSize = 200

  await requestExport({
    extraParams: { ...this.$route.query, page_size: flowPageSize },
    tableHead: HEAD_INFO,
    filename: '支付流水明细',
    typeConversions: [ // 设置单元格类型
      {
        columnCode: 'N', // 第 N 列
        columnType: 'n', // 设置为 数值 类型
        headLevel: 1 // N1 是表头,不设置
      }
    ]
  })
  
  this.exportPageLoading = false
  this.$message({
    type: 'success',
    message: '***数据已成功导出,请注意查收***',
    duration: 6000
  })
},

下面就来实现requestExport方法

js 复制代码
/**
 * 下载excel
 */
export const requestExport = async ({
  extraParams,
  tableHead,
  filename,
  typeConversions
}) => {
  return new Promise((resolve, reject) => {
    let total = 0
    
    // 关于 downloadWorker 文件的路径问题,后面会说
    const worker = new Worker('./downloadWorker.js')

    let list = []
    list[0] = Object.values(tableHead)

    // 启动 worker 请求数据
    worker.postMessage({
      extraParams,
      maxLength,
      tableHead,
      cookie: document.cookie, // 接口需要鉴权,根据自己业务鉴权方式修改
    })

    worker.onmessage = (e) => {
      console.log('Web Worker 返回的数据', e.data)
      list = e.data.list
      total = e.data.total

      // 导出excel
      exportExcel(list, filename, typeConversions)

      resolve()
    }
  })
}

worker 脚本,downloadWorker.js,该文件必须放在网站根目录

js 复制代码
onmessage = async (e) => {
  console.log('web work开始执行了', e.data)

  const { extraParams, maxLength, tableHead, cookie } = e.data
  let total = 0
  let page = 1
  const pageSize = 200
  let isLastPage = false
  let list = []
  list[0] = Object.values(tableHead)

  do {
    try {
      const params = {
        page_size: pageSize,
        ...extraParams,
        page
      }

      let url = 'http://****?'

      for (const key in params) {
        if (key && params[key]) {
          if (url[url.length - 1] === '?') {
            url += `${key}=${params[key]}`
          } else {
            url += `&${key}=${params[key]}`
          }
        }
      }

      const reqHeaders = {
        Cookie: cookie
      }

      const res = await fetch(url, { headers: reqHeaders }).then(function (response) {
        return response.json()
      })

      const { data } = res

      total = data.total

      // 处理数据
      const newList = data.list.map((row) => {
        const keys = Object.keys(tableHead)
        const ret = []
        keys.forEach((key) => {
          ret.push(row[key])
        })
        return ret
      })

      list = list.concat(newList)
      page++

      if (list.length + pageSize >= total) {
        isLastPage = true
      }
    } catch (error) {
      console.log('error', error)
    }
  } while (list.length < total)

  postMessage({ list, total })
}

ok,现在功能已经实现了

注意

new Worker('./downloadWorker.js') 中的 downloadWorker.js 需要是一个网络地址,或者放在当前网站的根目录

不过public目录下的文件不会编译,如果害怕源码泄露可以在打包时编译下再copy到dist目录

相关推荐
麒麟而非淇淋37 分钟前
AJAX 入门 day1
前端·javascript·ajax
2401_8581205339 分钟前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢44 分钟前
【Vue】VueRouter路由
前端·javascript·vue.js
随笔写2 小时前
vue使用关于speak-tss插件的详细介绍
前端·javascript·vue.js
史努比.2 小时前
redis群集三种模式:主从复制、哨兵、集群
前端·bootstrap·html
快乐牌刀片883 小时前
web - JavaScript
开发语言·前端·javascript
miao_zz3 小时前
基于HTML5的下拉刷新效果
前端·html·html5
Zd083 小时前
14.其他流(下篇)
java·前端·数据库
藤原拓远3 小时前
JAVAWeb-XML-Tomcat(纯小白下载安装调试教程)-HTTP
前端·firefox
重生之我在20年代敲代码3 小时前
HTML讲解(一)body部分
服务器·前端·html