日常开发中,数据导出到文件通常有两种方式:
- 在
后端处理
,以文件流
或者资源路径
的方式返回; - 后端返回数据,前端按需
处理后
再触发浏览器的下载事件,已保存到本地文件。
这里介绍后者的一种零依赖的实现方式。
保存内容到文件
通过 JS 创建一个隐藏的超链接,并主动点击触发下载事件。
/*** 保存内容到文件* @param {*} blob* @param {*} fileName*/
function saveToFile(blob, fileName = "下载文件.txt") {if (!(!!blob && blob.toString() == '[object Blob]')) {blob = new Blob([blob])}let link = document.createElement('a')link.href = window.URL.createObjectURL(blob) // 创建下载的链接link.download = fileName // 下载后文件名link.style.display = 'none'document.body.appendChild(link)link.click() // 点击下载window.URL.revokeObjectURL(link.href) // 释放掉blob对象document.body.removeChild(link) // 下载完成移除元素
}
生成CSV格式
CSV
(Comma-Separated Values,逗号分隔值)是一种纯文本格式,用于以表格形式存储数据,广泛应用于数据交换和处理,特别是在电子表格软件(如 Excel)和数据库系统之间的数据传输中。
处理起来也是比较简单,将二维数组中的子数组以分隔符(默认英文逗号)连接后以换行符拼接即可。这里需要注意特殊字段的处理:
长字符串
:特别是本身包含换行符(此时使用 ` 包裹)数组
:通过换行符拼接
const NEW_LINE = "\n"/*** 处理特殊字符,若参数为数组则进行换行转换* @Param {*} v*/
const fixToCsv = v=>{if(Array.isArray(v))return `"${v.join(NEW_LINE).replace(/"/g, "`")}"`if(typeof(v)==='string')return `"${v.replace(/"/g, "`")}"`return v
}/*** 保存到 CSV 默认编码为 UTF-8* @param {Array|Object} obj - 数据对象,可以是二维数组/Object(包含 headers、rows 属性)* @param {String} fileName - 文件名* @param {String} newLine - 换行符,默认 \n*/
function saveToCSV(obj, fileName = "下载文件", newLine=NEW_LINE) {let csvText = ""//参数为数组的情况if(Array.isArray(obj)){csvText = Array.isArray(obj[0])? obj.map(v=>v.map(fixToCsv).join(",")).join(newLine): obj.join(newLine)}else if(typeof(obj) === 'object'){let { headers, rows } = objif(!headers && !Array.isArray(headers)) throw Error(`[CSV导出] Object 类型的参数必须传递 headers 属性`)//写入标题栏csvText += headers.map(h=> typeof(h)==='object'?h.text:h).join(",") + newLinelet headerIds = headers.map(h=> typeof(h)==='object'? h.key:h)rows.forEach((row,rIndex)=>{csvText += headerIds.map(id=>fixToCsv(row[id])).join(",") + newLine})}else if(typeof(obj) === 'string')csvText = objsaveToFile(new Blob([csvText], { type: "application/csv;charset=utf-8" }), `${fileName}.csv`)
}
如何使用
// 拿到后台数据
const rows = fetch("{API}")
let csv = []
csv.push(["序号","名称","联系方式"])
// 处理数据
rows.forEach((row, i)=>{csv.push([`${i+1}`,row.name, row.phone])
})saveToCSV(csv, "用户导出")
之后浏览器会下载最终的 CSV 文件(此时需要注意弹窗拦截)。