前台实现csv文件下载思路
- 数据组装:将现有数据组装成csv文档格式类型,即逗号分隔每一列,换行符区分每一行
- 数据Blob表示:用二进制大对象方式表示已组装数据,并创建URL引用
- 前台触发机制:创建a标签或者img标签,利用其src能力触发前台下载
- 内存释放:下载完成后,释放已创建的URL引用对象以及自定义的a标签或image标签
函数实现
参数说明:
表头部分:
headers = [{
column:id,
title:'索引',
formatter:()=>{return ...}
}]
行数据:
rows = [{
index: 0,
userName: '',
userAge: ''
}]
fileName: 导出文件名称
const getCsvBlob = (headers, rows) => {
// 文件流开始标识
const BOM = '\uFEFF'
// 默认列分隔符
const columnDelimiter = ','
// 默认行分隔符
const rowDelimiter = '\r\n'
let csv = headers.reduce((previous, header) => {
return (previous ? previous + columnDelimiter : '') + (header.title || header.column)
}, '')
if(Array.isArray(rows) && rows.length > 0){
const columns = headers.map(header => header.column)
csv = columns.reduce((previous, row) => {
const rowCsv = columns.reduce((pre, column) => {
if(Object.prototype.hasOwnProperty.call(row,column)){
let cell = row[column]
if(cell !== null){
const header = headers.find(item => item.column === column)
if(header.formatter !== null && typeof(header.formatter) === 'function'){
cell = header.formatter(cell)
}
}
if(cell !== null){
// 若数据中本来就含有行分隔符,则用''替换
cell = cell.toString().replace(new RegExp(rowDelimiter,'g'),'')
// 若数据中本来就含有列分隔符,则用''包起来'
cell = new RegExp(columnDelimiter).test(cell) ? `'${cell}'` : cell
// reduce初始值为'',故第一次迭代时不会再行首加列分隔符。后边的遇到值为空或不存在的列要填充含空格的空白' ',则pre返回true,会加列分隔符
return pre ? pre + columnDelimiter : pre + ''
} else {
// 即使不存在该列也要填充空白,避免数据和表头错位不对应
return pre ? pre + columnDelimiter : pre + ''
}
}
},'')
return previous + rowDelimiter + rowCsv
},csv)
}
const blob = new Blob([BOM + csv], {
type: 'text/csv;charset=utf-8;'
})
return blob
}