需求背景:业务后台下载数据表格是调用后端接口直接下载的,但是数据量超过十万级别后,接口总是出现崩溃的情况,后端经过排查并没有找到原因,所以我就给出前端下载的方案
技术方案:由前端分页查询接口,最后汇总数据下载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目录