需求简介

很多时候,我们都会有这样一个业务。
将列表中的数据导出为excel。
这样做的目的是为了方便查看,同时可以保存在本地归档。
还可以将导出的Excel后的数据进行加工。

node-xlsx 的简单介绍

下载node-xlsx模块:cnpm install node-xlsx --save
node-xlsx 模块提供了excel 文件解析器和构建器。
它通过 xlsx.build 可以构建 xlsx 文件(就是将数据转为excel)
简单使用如下:
let buffer = xlsx.build([{name: 'excel工作薄的名称', data: '需要的数据-通常是数组'}]);
data 中的数据格式通常是这样的
data:[
{
name: "第1个工作薄的名称如:sheet",
data: [
["第1行第1列的数据", "第1行第2列的数据", "第1行第3列的数"],
["第2行第1列的数据", "第2行第2列的数据", "第2行第3列的数据"]
],
},
{
name: "第2个工作薄的名称如:sheet",
data: [
["第1行第1列的数据", "第1行第2列的数据", "第1行第3列的数"],
["第2行第1列的数据", "第2行第2列的数据", "第2行第3列的数据"]
],
}
] 同时node-xlsx也可以解析excel
xlsx.parse(filepath,{otherOptions})
{cellDates: true} 可以将将时间格式转化为 ISO 8601
ISO 8601:是全世界日期和时间相关的数据交换的国际标准。
这个标准的目标是在全世界范围的通信中提供格式良好的、无歧义的时间和日期表示。

node-xlsx 构建 xlsx 文件[将数据转化为excel]

//引入生成excel的依赖包
const xlsx = require("node-xlsx");
let fs = require("fs");
const list = [
{
name: "sheet", // 工作薄的名称
data: [
["第1行第1列", "第1行第2列", "第1行第3列"],
["第2行第1列", "第2行第2列", "第2行第3列"]
],
},
// 如果多个工作薄, 就是多个对象。格式如上
];
// 使用提供的构建 xlsx 文件的方法
const buffer = xlsx.build(list);
fs.writeFile("导出excel的名称.xlsx", buffer, function (err) {
if (err) {
console.log(err, "导出excel失败");
} else {
console.log("导出excel成功!");
}
});

需要注意的2点

需要注意的1点:如果当前目录下有一个excel的名称与你现在导出的名称相同。
就会出现覆盖,后面的覆盖前面的数据。
需要注意的2点:还有一个注意的点是:如果你把导出文件名相同的excel打开。
就会出现导出失败: 提示为:s[Error: EBUSY: resource busy or locked]

如何设置列宽呢?

刚刚我们虽然导出成功。
但是我们发现列宽太窄。我们需要设置一下列宽。
我们需要通过一个配置参数来处理
我们可以通过配置项 sheetOptions 来处理
通过 xlsx.build 的第2个参数来处理
const sheetOptions = {'!cols': [{wch: 20}, {wch: 30}]}; //设置宽度
var buffer = xlsx.build([{name: 'mySheetName', data: data}], {sheetOptions});
//引入生成excel的依赖包
const xlsx = require("node-xlsx");
let fs = require("fs"); const data = [
["姓名", "地址", "性别", '联系方式'],
["张三", "四川", "男", '18485645634'],
];
// wch 设置列宽
const sheetOptions = {'!cols': [{wch: 20}, {wch: 30}, {wch: 40}, {wch: 50}]};
// mySheetName 表名 data导出的数据 sheetOptions 是配置项
var buffer = xlsx.build([{name: 'mySheetName', data: data}], {sheetOptions});
fs.writeFile("导出excel的名称.xlsx", buffer, function (err) {
if (err) {
console.log(err, "导出excel失败");
} else {
console.log("导出excel成功!");
}
});

03png

实现导出下载功能-node后端代码

//引入生成excel的依赖包
const xlsx = require("node-xlsx");
let fs = require("fs");
let express = require('express');
let router = express.Router();
// 引入连接数据库的模块
const connection=require("./connectmysql.js")
// 查询
router.get('/export', function (req, res) {
// 写一个简单的查询语句
const sqlStr = 'select * from account';
//执行sql语句
connection.query(sqlStr, (err, data) => {
if (err) {
res.send({
code: 1,
msg:'查询失败'
});
throw err
} else {
exportFun((obj) => {
console.log('obj',obj)
// 设置响应头
res.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
);
res.setHeader('Content-Disposition', 'attachment; filename=test.xlsx');
// 将 Excel 文件的二进制流数据返回给客户端
res.end(obj.data, 'binary');
})
}
})
}) function exportFun(callback) {
const data = [
["用户名", "密码", "出生年月"],
["张三", "qwer090910989", "1999-02-12"]
];
// wch 设置列宽
const sheetOptions = {'!cols': [{wch: 30}, {wch: 30}, {wch: 30}]};
// mySheetName 表名 data导出的数据 sheetOptions 是配置项
var buffer = xlsx.build([{ name: 'mySheetName', data: data }], { sheetOptions });
callback({
success: true,
data:buffer,
info:'导出excel成功'
})
} module.exports = router;

实现导出下载功能-前端代码

<el-button @click="downLoadHandler">下载</el-button>

methods: {
downLoadHandler(){
axios({
method: 'get',
url: 'http://127.0.0.1:666/download/export',
responseType: 'blob' // 资源的类型
}).then(res => {
console.log('返回来的数据', res)
this.downLoadFile(res.data, 'excel名称.xlsx', () => {})
}).catch(err => {
console.log(err)
})
}
}

刚刚我们知道了返回来的数据格式是Blob类型的。
现在只需要我们进行一次转换。然后创建a标签。
模拟点击事件进行下载
downLoadFile(fileData, fileName, callBack) {
// 创建Blob实例 fileData 接受的是一个Blob
let blob = new Blob([fileData], {
type: 'applicationnd.ms-excel',
})
if (!!window.ActiveXObject || 'ActiveXObject' in window) {
window.navigator.msSaveOrOpenBlob(blob, fileName)
} else {
// 创建a标签
const link = document.createElement('a')
// 隐藏a标签
link.style.display = 'none'
// 在每次调用 createObjectURL() 方法时,都会创建一个新的 URL 指定源 object的内容
// 或者说(link.href 得到的是一个地址,你可以在浏览器打开。指向的是文件资源)
link.href = URL.createObjectURL(blob)
console.log('link.href指向的是文件资源', link.href)
//设置下载为excel的名称
link.setAttribute('download', fileName)
document.body.appendChild(link)
// 模拟点击事件
link.click()
// 移除a标签
document.body.removeChild(link)
// 回调函数,表示下载成功
callBack(true)
}
}



关于axios.get() 置请求头responseType:'blob'不生效

之前在遇见一个问题。
就是关于axios.get() 置请求头responseType:'blob'是不生效。
这里我想说明一下,其实也是会生效的。只是可能设置的方式不正确。
如果你是这样写的,确实不会生效,并且下载还会出现一些乱七八糟的情况。
// 错误的写法 这种设置类型会失败的。
// axios.get() 就没有第三个参数。如果有是我们自定义的。它本身是没有的
axios.get('url', {}, { responseType: 'blob' }).then((response) => {
console.log('返回来的数据', response)
}).catch(function (error) {
console.log(error);
});

这个时候,我们发现返回来的不再是 blob 类型。
那为什么会出现这样的原因呢?
因为我们上面设置类型压根就没有设置成功。
不应该设置在第3个参数中(它本身是没有的第3个参数。第3个是我们自定义的)。应该放置在第2个参数中
正确的设置方法
axios.get(url[, config]) // 将设置数据类型放置在 第2个参数中
axios.get('url', { responseType: 'blob' }).then((response) => {
console.log('返回来的数据', response)
this.downLoadFile(response.data, 'excel.xlsx', () => {})
}).catch(function (error) {
console.log(error);
});



mockjs会导致文件下载失败及原因

如果你的项目中有使用mockjs
那么下载肯定会失败的。因为mockjs初始化了responseType
从而导致下载失败。

验证 mockjs 会导致下载失败

当我们的项目使用了mockjs之后。
返回来的数据不再是 Blob。
我们现在在项目中使用了mockjs 看看文件是否可以正常的下载成功
created() {
Mock.mock("/api/login", {
code: 200,
msg: "登录成功",
user: { name: "李四", age: 18, sex: '男' },
token: 'token2023',
})
}
<el-button @click="downLoadHandler">下载</el-button>
downLoadHandler() {
axios.get('http://127.0.0.1:666/download/export',
{ responseType: 'blob' }).then((response) => {
console.log('返回来的数据', response)
this.downLoadFile(response.data, 'excel.xlsx', () => {})
}).catch(function (error) {
console.log(error);
});
}

引入 mockjs 之后,文件果然下载失败了。
那怎么解决这个问题呢? 注释掉 mockjs 就可以了

node-xlsx 结合 multer 实现excel导入

multer:是一个node.js中间件,主要用于上传文件。
安装 npm install --save multer
multer的基本用法
let multer = require('multer');
let Storage = multer.diskStorage({
// 存储文件的位置
destination: (req, file, callback) => {
//指定当前这个文件存放的目录,如果没有这个目录将会报错
callback(null, 'public/upload');
},
// 文件中的文件名称
filename: (req, file, callback) => {
// 文件命名
callback(null, '可以重新命名文件');
}
});
每个文件都包含以下信息:
fieldname 表单中指定的字段名称
originalname 用户计算机上的文件的名称
filename 文件中的文件名称
path 上传文件的完整路径
path 上传文件的完整路径 其他配置项
limits:一个对象,指定一些数据大小的限制。
limits:{
files:'文件最大数',
fileSize: '文件最大长度 (字节单位byte)' 1MB=1024KB= 1048576 byte
node-xlsx怎么解析excel
//引入模块
let xlsx = require('node-xlsx'); // 解析 xlsx 文件,处理时间否者时间会发生变化
let sheets = xlsx.parse('./test.xlsx');
// 获取工作薄中的数据
// 数据格式为:[ { name: 'mySheetName', data: [ [Array], [Array] ] } ]
console.log('数据格式为:',sheets);
let arr = []; // 全部表中的数据
sheets.forEach((sheet) => {
for (let i = 1; i < sheet['data'].length; i++) {
//excel第一行是是表头,所以从1开始循环
let row = sheet['data'][i]; // 获取行数据
if (row && row.length > 0) {
// moment处理 ISO 8601格式的时间,
arr.push({
name: row[0], // row[0]对应表格里A列
password: row[1], // row[1]对应表格里B列
brith:row[2], // row[2]对应表格里C列
});
}
}
console.log('读取的数据', arr)
});

如何处理时间读取的时候发生的变化
在 xlsx.parse方法的第二个参数中设置 cellDates: true
可以将时间转为 ISO 8601 如下:
let sheets = xlsx.parse(fileUrl,{cellDates: true});

使用 moment 来处理 ISO 8601格式的时间 YYYY-MM-DD HH:mm
// moment处理 ISO 8601格式的时间,
let dateTime = moment(row[2]);
dateTime.utc().format('YYYY-MM-DD HH:mm') ,
我们发现时间虽然是 YYYY-MM-DD HH:mm
但是与我们表格中的数据相差了8个小时。
怎么处理?别急。我们可以让 UTC 偏移为 8个小时

13png

使用偏移与时间时间保持一致
let dateTime = moment(row[2]);
brith:dateTime.utc('+8:00').format('YYYY-MM-DD HH:mm')

node-xlsx 实现对excel的解析写入数据库
let express = require('express');
let multer = require('multer');
let xlsx = require('node-xlsx');
let moment = require('moment');
let fs = require('fs');
let router = express.Router();
let Storage = multer.diskStorage({
destination: (req, file, callback) => {
// 指定当前这个文件存放的目录
// 如果没有这个目录将会报错
callback(null, 'public/upload');
},
filename: (req, file, callback) => {
console.log('fieldname',file)
// 文件命名:当前时间戳 + "_" + 源文件名称
callback(null, new Date().getTime() + '_' + file.originalname);
}
});
// 我们这里支持多文件上传,上传名为 file。
let upload = multer({
storage: Storage,
limits: {
fileSize: 1024 * 1024*10, // 限制文件大小
files: 5 // 限制上传数量
}
}).array('file', 99999); router.post('/upload', function (req, res) {
upload(req, res, (err) => {
if (err) {
res.send({ code:'1', msg:'导入失败', err:err})
} else {
// 获取这个文件的路径
const fileUrl = req.files[0].path;
// 解析 xlsx 文件,处理时间否者时间会发生变化
var sheets = xlsx.parse(fileUrl,{cellDates: true});
// 获取工作薄中的数据
// 数据格式为:[ { name: 'mySheetName', data: [ [Array], [Array] ] } ]
console.log('数据格式为:',sheets);
var arr = []; // 全部表中的数据
sheets.forEach((sheet) => {
for (var i = 1; i < sheet['data'].length; i++) {
//excel第一行是是表头,所以从1开始循环
var row = sheet['data'][i]; // 获取行数据
if (row && row.length > 0) {
// moment处理 ISO 8601格式的时间,
var dateTime = moment(row[2]);
arr.push({
name: row[0], // row[0]对应表格里A列
password: row[1],// row[1]对应表格里B列
// 使用偏移与时间时间保持一致
brith: dateTime.utc('+8:00').format('YYYY-MM-DD HH:mm'),
});
}
}
});
// 读取成功1分钟后将这个文件删除掉
setTimeout(() => {
fs.unlinkSync(fileUrl);
}, 1000 * 60);
console.log('解析后的数据',arr )
res.send({ code:'0', msg:'导入成功',data: arr,total: arr.length})
}
});
});
module.exports = router;

前端代码
<h2>文件上传</h2>
<el-upload class="upload-demo" action="https"
:http-request="uploadExcelFile">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload> uploadExcelFile(file) {
let formdata = new FormData();
console.log(file);
formdata.append("file", file.file);
axios.post('http://127.0.0.1:666/upload/upload',
formdata, {
'Content-type': 'multipart/form-data'
}
).then(function (response) {
console.log(response);
}).catch(function (error) {
console.log(error);
});
}

node使用node-xlsx实现excel的下载与导入,保证你看的明明白白的更多相关文章

  1. 使用XSSFWork创建的xlsx后缀Excel文件无法打开

    使用XSSFWork创建的xlsx后缀Excel文件无法打开 标签: POIExcelmicrosoftxlsx 2015-04-21 10:49 1170人阅读 评论(2) 收藏 举报 分类: Ja ...

  2. thinkphp3.2.3 excel导出,下载文件,包含图片

    关于导出后出错的问题 https://segmentfault.com/q/1010000005330214 https://blog.csdn.net/ohmygirl/article/detail ...

  3. WARN node unsupported "node@v6.11.2" is ......(windows系统更新node版本)

    问题: 使用npm下载文件时报错:WARN node unsupported "node@v6.11.2" is incompatible with electron@^7.1.9 ...

  4. phpexcel生成excel并下载

    Loader::import('PHPExcel.Classes.PHPExcel'); // tp5中只需将phpexcel文件放入extend文件夹中,即可采用该方法引入,需要先 use thin ...

  5. [Node.js] Node + Redis 实现分布式Session方案

    原文地址: http://www.moye.me/?p=565 Session是什么? Session 是面向连接的状态信息,是对 Http 无状态协议的补充. Session 怎么工作? Sessi ...

  6. cocos2d-x中Node与Node层级架构

    Cocos2d-x采用层级(树形)结构管理场景.层.精灵.菜单.文本.地图和粒子系统等节点(Node)对象.一个场景包含了多个层,一个层又包含多个精灵.菜单.文本.地图和粒子系统等对象.层级结构中的节 ...

  7. 在linux环境下配置node:node + npm + forever

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3574582.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  8. Hadoop ->> Name node/Data node和Job tracker/Task tracker的区别

    刚好看到关于Name node/Data node和Job tracker/Task tracker的解释,一开始有点混淆,以为说Job tracker必须运行在Name node上,他们俩有依赖或者 ...

  9. Cocos2d-x Lua Node与Node层级架构

    Cocos2d-x Lua采用层级(树形)结构管理场景.层.精灵.菜单.文本.地图和粒子系统等节点(Node)对象.一个场景包含了多个层,一个层又包含多个精灵.菜单.文本.地图和粒子系统等对象.层级结 ...

  10. cocos2d-x 3.0 Node与Node层级结构

    节点解释: 节点是场景图的基本元素.场景图的基本元素必须是节点对象或者是节点对象的子类. 其中主要可以看到Layer.MenuItem.Scene.Sprite.TMXTiledMap(解析and渲染 ...

随机推荐

  1. Filbeat采集nginx-ingress日志

    一.创建configmap配置文件 注:filebeat6以上版本需要将prospectors改为inputs,paths下指定的nginx-ingress日志路径匹配模式以及hosts指定的kafk ...

  2. 初识 Linux Shell

    初识 Linux Shell 本书学习的第一步,就是要找到 Linux 终端的所在位置.目前较常见的图形化终端有 Konsole.Gnome terminal.xterm 等几种.一般安装后在各个发行 ...

  3. Python笔记(4)——元组(Python编程:从入门到实践)

    元组 1. 元组:不可变的列表.元组一经创建不能被修改. 2. 表示:用圆括号()来表示,并用逗号来分隔其中的元素.可通过索引访问其元素. 3. 访问:访问列表元素,指出元组的名称,再指出元素的索引, ...

  4. DBeaver通过phoenix连接云主机的hbase

    准备 1.云主机上已经安装好jdk.hadoop.hbase.zookeeper.phoenix,并且在主机上测试连接成功.可参考 https://blog.csdn.net/shangxindeku ...

  5. SQLServer数据库执行时间

    with kk AS( SELECT TOP 2000 total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数], qs.tot ...

  6. DHCP分配IP的流程

    1.DHCP客户端以广播的形式发送DHCP Discover报文 2.所有的DHCP服务端都可以接收到这个DHCP Discover报文,所有的DHCP服务端都会给出响应,向DCHP客户端发送一个DH ...

  7. FreeType 矢量字体 测试移植(1)

    之前有做过 ascii 和汉字库的字体点阵在lcd上显示的例子,都是按照指定大小的字库的点阵来显示的,所以一但选定了字体文件后,就固定了大小,不可变化,当然也可以存放各种 大小的字体文件,但这样的话就 ...

  8. vim的visual模式和列编辑

    有三种方式进入visual模式: 1> 在普通模式下输入v(小写),底部提示信息为VISUAL,编辑粒度为字符 通过方向键或者HJKL调整选择的字符范围. 输入d,删除选中字符: 输入y,复制当 ...

  9. C语言基础题 22年5月

    (十六进制 %x  20.0f是%f  long 是%ld  unsigned int %#o  ) 编程题: 整数浮点数的上溢下溢 strcpy函数代码 int search(char *s, ch ...

  10. 【Python】Python3环境安装

    编译安装 安装依赖 yum install wget gcc make zlib-devel openssl openssl-devel readline-devel wget "https ...