共计 2195 个字符,预计需要花费 6 分钟才能阅读完成。
当一个零碎须要导出动态数据时,有时候首选计划是:由服务端实时生成 csv 或 Excel 格局的文件,而后用二进制流的模式返回给前端,这时候会遇到一些问题。其中最大的问题是,如果数据量很大,解决时长,超过了网关设置的超时工夫怎么办?
这时候不得不寻找其余的下载 / 导出计划。其实解决方案很多,这里举荐一个比拟全能的计划,而且实现起来不简单。
StreamSaver.js 能够解决问题
在这个计划里,服务端只须要提供一个分页接口,前端循环调用该接口拿数据,解析后写入同一个文件,甚至能够压缩成 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 文件成果如下:
正文完
发表至: javascript
2021-02-07