前端文件下载的几种方式
// 1. 直接下载 (针对一些浏览器无法识别的文件格式,直接在地址栏上输入URL即可触发浏览器的下载功能)
window.location.href = URL;
window.open(URL);
// 2. 直接下载 使用A标签download属性
<a href="file.xlsx" download="文件名">文件下载</a>
// 3. 通过触发JS直接下载
function dowloadFile(fileName, link) {
let DownloadLink= document.createElement('a')
DownloadLink.style = 'display: none;'
DownloadLink.download = fileName
DownloadLink.href = link
document.body.appendChild(DownloadLink)
DownloadLink.click();
document.body.removeChild(DownloadLink)
}
// 通过 ajax 请求, 获取流数据的方式导出
// 获取数据时,必须加上(responseType: "blob"), 表示后台穿过来的数据用 'blob' 对象接收
axios.post("ajaxUrl", { responseType: "blob" })
// 下载流文件公共方法
downloadBlobFile(response, fileName = '') {
const fileInfo = response.headers['content-disposition'].split(';')[1]
let _fileName = fileName || decodeURIComponent(fileInfo && fileInfo.split('=')['1'])
let blob = new Blob([response.data])
let href = window.URL.createObjectURL(blob) //创建下载的链接
if (window.navigator.msSaveBlob) {
try {
window.navigator.msSaveBlob(blob, _fileName)
} catch (e) {
console.error(e)
}
} else {
// 谷歌浏览器 创建a标签 添加download属性下载
let downloadElement = document.createElement('a')
downloadElement.href = href
downloadElement.target = '_blank'
downloadElement.download = _fileName //下载后文件名
document.body.appendChild(downloadElement)
downloadElement.click() //点击下载
document.body.removeChild(downloadElement) //下载完成移除元素
window.URL.revokeObjectURL(href) //释放掉blob对象
}
}
SheetJS 生成/解析 Excel 介绍
SheetJs Github
使用SheetJS
的xlsx.js
实现生成excel
表格
纯JS即可读取/生成excel
,功能强大,支持多种格式,兼容性高
xlsx.js
提供了一个中间层用于操作数据,将不同类型的文件抽象成同一个js
对象,从而规避了操作不同种类数据之间的复杂性。
基本概念
- 工作簿 workbook 对象:指的是整份 Excel 文档。
- 工作表 worksheet 对象:指的是 Excel 文档中的表。可以包含很多张表,每张表对应一个 worksheet 对象
- 单元格 cell 对象:指的就是 worksheet 中的单元格,一个单元格就是一个 cell 对象
{
"SheetNames": ["sheet1"],
"Sheets": {
"Sheet1": {
!ref: “A1:J3”, // 单元格范围
!rows: [], // 表格行属性
!cols: [
{wpx: 90}
], // 单元格属性
!merges: [], // 单元格合并规则
A1/B1/C1...: {}, // 对应excel中的每一个具体的单元格
}
}
}
单元格对象
sheet
中的: A1/B1/C1...: 单元格描述信息
属性 | 描述 |
---|---|
t | 表示内容类型 { s: ‘string’, n: 'number', b: 'boolean', d: 'date' } |
v | 原始值 |
f | 公式,如: B2 + B3 |
h | HTML内容 |
w | 格式化后的内容 |
r | 富文本内容 |
!merges合并单元格
// 数组的每一项,代表其中一个单元格的合并要求
ws['!merges'] = [
[s: {r: 0, c: 0}, e: {r: 1, c: 0}],
[s: {r: 0, c: 1}, e: {r: 1, c: 1}]
]
// s: 代表合并的起始位置
// e: 代表合并的结束位置
// r: 代表行数
// c: 代表列数
// 数组的第一个对象,表达的含义为: 以0行0列(对应单元格A1)作为起始,以1行0列(对应单元格A2)作为结束,合并这些单元格
单元格地址
xlsx
使用有两种方式来描述操作中的单元格区域:
- 一种是单元格地址对象 (Cell address object)
// 地址对象描述的是一个起始坐标(从0开始)到结束坐标之间的范围
const start = { c: 0, r: 0 };
const end = { c: 1, r: 1 };
- 另一种是地址范围 (Cell range)
// 地址范围就是Excel中的引用样式
const range = 'A1:B2'
worksheet Object 工作表对象
{
"sheet1": {
"A1": {}, // 单元格对象
"!ref": "A1:B5", // 表示工作表范围
“!cols”: [
[ wpx: 200 ], // 设置第1列列狂为200箱数
[ wch: 50 ], // 设置第2列列宽为50字符
],
"!merges": [
{
s: { c: 1, r: 1 }, // B2
s: { c: 3, r: 3 }, // D4
}
], // 合并单元格范围数据
"!freeze": {
"xSplit": "1", // 冻结列
“ySplit": "1", // 冻结行
}
}
}
xlsx.js
引入
// 以 script 标签的形式
<script lang="javascript" src="dist/xlsx.full.min.js"></script>
// 使用 npm 安装 xlsx 模块
npm install xlsx --save
// 引入
import XLSX from 'xlsx'
// 或
const XLSX = require('xlsx')
xlsx.js
的一些常用方法
读取数据并解析:
方法名 | 描述 |
---|---|
XLSX.readFile(filename[, opts]) |
获取数据 |
xlsx.readFileSync(filename[, opts]) |
异步获取数据 |
XLSX.read(data[, opts]) |
解析数据 |
数据导出:
方法名 | 描述 |
---|---|
XLSX.write(workbook[, opts]) |
用来写入工作簿 workbook |
XLSX.writeFile(workbook, filename[, opts]) |
把 workbook 写入到特定的文件 filename 中 |
在XLSX.utils
对象中有一些方法可以对单元格和单元格范围进行转化
// 编码行号
XLSX.utils.encode_row(2); // "3"
// 解码行号
XLSX.utils.decode_row("2"); // 1
// 编码列
XLSX.utils.encode_col(2); // "C"
// 解码列
XLSX.utils.decode_col("A"); // 0
// 编码单元格
XLSX.utils.encode_cell({ c: 1, r: 1 }); // "B2"
// 解码单元格
XLSX.utils.decode_cell("B1"); // { c: 1, r: 0 }
// 编码单元格范围
XLSX.utils.encode_range({
s: { c:1, r: 0 },
e: { c:2, r: 8 }
}); // "B1:C9"
// 解码单元格范围
XLSX.utils.decode_range("B1:C9");
// {s: {r: 1, r: 0}, e: {c: 2, r: 8}}
操作工作簿
// 1. 创建一个工作簿
const workBook = XLSX.utils.book_new();
// 2. 创建工作表对象的几种方式
// 使用二维数组创建一个工作表对象
const workSheet = XLSX.utils.aoa_to_sheet(data);
// 使用对象数据创建一个工作表对象
const workSheet = XLSX.utils.json_to_sheet(data);
// 根据已渲染好的表格,转成工作表数据,会自动识别对应的单元格合并数据
const workSheet = XLSX.utils.table_to_sheet("表格ID")
// 向工作簿追加一个工作表
XLSX.utils.book_append_sheet(workBook, workSheet, "工作表名称")
数据填充 - 创建工作表
工作表是实际存放数据的地方,大部分情况下我们的操作都是在对工作表对象的小左。
aoa_to_sheet
根据二维数组创建工作表
var data1 = [
['主要信息', null, null, '其它信息'],
['姓名', '性别', '年龄', '注册时间'],
['张三', '男', 18, new Date()],
['李四', '女', 22, new Date()]
];
// 1. 新建一个工作簿
let workbook = XLSX.utils.book_new();
// 2. 生成一个工作表,
// 2.1 aoa_to_sheet 把数组转换为工作表
let sheet1 = XLSX.utils.aoa_to_sheet(data1);
// 3. 设置单元格合并
sheet1['!merges'] = [
// 设置A1-C1的单元格合并
{ s: {r: 0, c: 0}, e: {r: 0, c: 2 }}
];
// 4.在工作簿中添加工作表
XLSX.utils.book_append_sheet(workbook, sheet1, "工作表名称");
// 5.输出工作表,由文件名决定的输出格式
XLSX.writeFile(workbook, "默认的导出文件名称.xlsx");
**json_to_sheet
根据二维数组创建工作表: **
const data = [
{ L: 8, O: 5, V: 2, E: 7, ID: 'i_8527' },
{ L: 1, O: 5, V: 9, E: 2, IDL 'i_15926'}
]
// 基础版: 会自动提起键名作为表头
let worksheet = XLSX.utils.json_to_sheet(data)
展示结果:
L | O | V | E | ID |
---|---|---|---|---|
8 | 5 | 2 | 7 | i_8527 |
1 | 5 | 9 | 2 | I_15926 |
排序并自定义表头
// 自定义表格的名称
const headerDisplay = {
L: "L栏", O: "O栏", V: "V栏", E: "E栏", ID: "ID栏"
}
const newData = [headerDisplay, ...data]
let worksheet = XLSX.utils.json_to_sheet(data, {
header: ['ID'] // 表格列排序, 示吧ID移动到最前面
skipHeader: true // 忽略表格原有的表头
})
ID | L | O | V | E |
---|---|---|---|---|
i_8527 | 8 | 5 | 2 | 7 |
I_15926 | 1 | 5 | 9 | 2 |
数据填充 - 修改工作表数据
// 创建工作表
const worksheet = XLSX.utils.json_to_sheet([
{ '列1': 1, '列2': 2, '列3': 3 },
{ '列1': 4, '列2': 5, '列3': 6 }
], {
header: ['列3', '列2', '列1'], // 排序
skipHeader: true
})
// 修改成新数据 sheet_to_aoa 二维数组方式
XLSX.utils.sheet_to_aoa(worksheet, [
['姓名', '年龄', '地址'],
["张三", 26, "北京"],
["小右", 18, "成都"]
],{
origin: 'A1' // 从A1开始增加内容
})
// -------------------------------
// 修改成新数据 sheet_to_json 方式
XLSX.utils.sheet_to_json(worksheet, [
{ "年龄": 26, "地址": "深圳", "姓名": "张三" },
{ "年龄": 18, "地址": "成都", "姓名": "小右" },
], {
origin: "A1",
header: ["姓名", "年龄", "地址"],
skipHeader: true
})
姓名 | 年龄 | 地址 |
---|---|---|
张三 | 26 | 深圳 |
小右 | 18 | 成都 |
导出Excel文件
根据已经渲染好的表格进行导出Excel :会自动识别单元格合并
/**
* arrTableId 传参 多条表示有多个表
* [
* {id: 'excel1', sheetName: 'sheet1'},
* {id: 'excel2', sheetName: 'sheet2'}
* ]
*/
export function exportTableToExcel(arrTableId, excelFileName) {
const workbook = XLSX.utils.book_new();
// 循环得到每一张表的 sheet,并添加到 workbook 中
arrTableId.forEach((item) => {
const tableEle = document.getElementById(item.id);
const ws = XLSX.utils.table_to_sheet(tableEle);
XLSX.utils.book_append_sheet(workbook, ws, item.sheetName)
})
// 导出Excel
XLSX.writeFile(workbook, excelFileName)
}
// 使用
const exportParams = [{ id: "xGrid1", sheetName: "工作表名称" }]
this.exportTableToExcel(exportParams, "导出后保存的文件名称.xlsx");
根据 aoa_to_sheet
和json_to_sheet
创建的工作表进行导出,复杂表头需要提供合并的 !merges
数据
// 复杂表头示例
const workbook = XLSX.utils.book_new();
const data = [
// 特别注意: 合并的地方后面预览 null
[ "主要信息", null, null, "其他信息" ],
[ "姓名", "性别", "年龄", "注册时间" ],
[ "张三", "男", 26, new Date() ],
[ "李四", "女", 18, new Date() ]
]
const sheet = XLSX.utils.aoa_to_sheet(data);
sheet['!merges'] = [
// 设置A1-C1的单元格合并
{ s: { r: 0, c: 0 }, e: { r: 0, c: 2 }}
]
XLSX.utils.book_append_sheet(workbook, ws, sheet)
// 导出Excel
XLSX.writeFile(workbook, excelFileName)
合并单元格方案
需要自己提供单元格合并范围的数据
const mergesArr = ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2']
const range = ["A1:A3", "B1:F1", "B2:B3", "C2:F2"].map((item) => {
return XLSX.utils.decode_range(item);
});
sheet1["!merges"] = range;