浏览器端js主导的导出动态数据
当一个系统需要导出动态数据时,有时候首选方案是:由服务端实时生成csv或Excel格式的文件,然后用二进制流的形式返回给前端。
比如这里说的方法: nodejs+koa以流的形式返回数据
这时候会遇到一些问题。其中最大的问题是,如果数据量很大,处理时长,超过了网关设置的超时时间怎么办?
这时候不得不寻找其他的下载/导出方案。其实解决方案很多,这里推荐一个比较全能的方案,而且实现起来不复杂。
StreamSaver.js 可以解决问题,而且它对文件大小,没有限制。
FileSaver.js 也能做到,但它文件大小受限于前端可用内存和Blob允许的最大值即2G
在这个方案里,服务端只需要提供一个分页接口,前端循环调用该接口拿数据,解析后写入同一个文件,甚至可以压缩成zip格式。
优点
前端可以给请求加上任意参数,也可以对接口返回的数据做解析判断,还可以中断下载。复杂点的话,也能做到断点续传。
缺点
多次请求会增加网关的负载。不过考虑到下载功能不是频繁使用,在大多数情况下还是可行的。分页合理的话,能最大限度降低多次http请求的性能损耗。
使用streamsaver
还是挺自由的,下面是利用它封装了一个简易使用方法,可以满足一般的需求。更多使用方法,参考官方文档:https://github.com/jimmywarti...
函数声明:
/**
* 下载大文件
* 文档: https://github.com/jimmywarting/StreamSaver.js
* */
import streamSaver from 'streamsaver'
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
/**
* 注意,该方法可能会延迟七八秒后才调起浏览器的下载弹窗,所以用的时候记得做些loading提示
*
* @param fileName
* @param getData 该函数返回字符串,则会在之前返回的字符串最后加上这字符串。支持 \n 换行符。返回null或抛出错误时,才会完成下载。
* @param opt
* @param opt.onComplete 当下载完成后会执行,如果 getData 抛出错误,会把错误从这里返回。如果是用户手动取消, error === 'USER_CANCEL'。如果是下载成功,error===undefined
* @return 返回的对象里有些功能,有用的是 .abort() 函数,可以主动取消下载,用户下到一半的文件直接没了
* */
export default (
fileName: string,
getData: () => Promise<string | null>,
opt?: {
onComplete?: (error?: any | 'USER_CANCEL') => void
addBOM?: boolean
},
) => {
const fileStream = streamSaver.createWriteStream(fileName)
const writer = fileStream.getWriter()
if (opt?.addBOM !== false) {
// 文本文件如果没有 BOM 头,用微软的软件打开有时候会乱码,通常这个方法是用来下载csv的,所以加上比较好
writer.write(encode('\uFEFF')).then()
}
go(writer, getData, opt?.onComplete).then()
return writer
}
async function go(
writer: WritableStreamDefaultWriter,
getData: () => Promise<string | null>,
onComplete?: (error?: any | 'USER_CANCEL') => void
) {
let data = null
let getError
try {
data = await getData()
} catch (e) {
getError = e
console.error('[downloadLargeFile error]', e)
}
if (data === null) {
await writer.close()
onComplete?.(getError)
return
}
let hadResolve = false
if (onComplete) {
// 延时检查能否写入成功,不知道延时够不够
setTimeout(() => {
if (!hadResolve) {
writer.abort().then()
onComplete?.('USER_CANCEL')
}
}, 2000)
}
await writer.write(encode(data))
// 如果用户取消下载,上一行不会 resolve,所以下面就不会执行了
hadResolve = true
go(writer, getData, onComplete).then()
}
使用方法:
const wait = (msec: number) => new Promise((resolve) => setTimeout(resolve, msec))
let i = 0
downloadLargeFile(
`下载测试.txt`,
async () => {
// 这个函数内部可以做任何异步操作
if (i >= 20) {
return null
}
i += 1
await wait(1000)
console.log(111, i)
return `${i.toString()}\n`
},
{
onComplete: (error) => {
console.log('完成', error)
},
},
)
以上示例下载的txt文件效果如下:
黒之染
几年半个人练习生,喜欢ctrl c、ctrl v、delete
被 1 篇内容引用
推荐阅读
jest如何执行单组测试用例
假如有这个文件tests/test.test.ts: {代码...} 我只想运行里面的t2,则可以这样: {代码...} 跨级别之间用空格分隔即可。相关文档:[链接]
黒之染赞 2阅读 1.6k
正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...
寒青赞 57阅读 8.6k评论 11
JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...
jenemy赞 49阅读 7.3k评论 12
再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...
libinfs赞 42阅读 6.9k评论 12
CSS 绘制一只思否猫
欢迎关注我的公众号:前端侦探练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(>^ω^<...
XboxYan赞 47阅读 3.3k评论 14
「多图预警」完美实现一个@功能
一天产品大大向 boss 汇报完研发成果和产品业绩产出,若有所思的走出来,劲直向我走过来,嘴角微微上扬。产品大大:boss 对我们的研发成果挺满意的,balabala...(内心 OS:不听,讲重点)产品大大:咱们的客服 I...
wuwhs赞 32阅读 3.5k评论 5
还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库
XboxYan赞 35阅读 2.7k评论 2
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。