Nodejs包之node-xlsx
支持读写Excel的node.js模块 1. node-xlsx: 基于Node.js解析excel文件数据及生成excel文件,仅支持xlsx格式文件; 2. excel-parser: 基于Node.js解析excel文件数据,支持xls及xlsx格式文件; 3. excel-export : 基于Node.js将数据生成导出excel文件,生成文件格式为xlsx; 4. node-xlrd: 基于node.js从excel文件中提取数据,仅支持xls格式文件。
我们先来个简单小demo吧:
首先要安装这个包依赖: npm install -save nodejs-xlsx
然后就是看下面代码了:
var xlsx = require('node-xlsx');
var fs = require('fs');
//读取文件内容
var obj = xlsx.parse(__dirname+'/test.xlsx');
var excelObj=obj[0].data;
console.log(excelObj);
var data = [];
for(var i in excelObj){
var arr=[];
var value=excelObj[i];
for(var j in value){
arr.push(value[j]);
}
data.push(arr);
}
var buffer = xlsx.build([
{
name:'sheet1',
data:data
}
]);
//将文件内容插入新的文件中
fs.writeFileSync('test1.xlsx',buffer,{'flag':'w'});
实际项目中通常用 buffer 流传递数据表进行解析:
实际场景 1 为:客服端上传规定格式的模板表格数据(这里只有一列为【编号】)
exports.uploadExcel = function (args, req, res, next) {
/**
* uploadExcel 批量上传商品 Excel
**/
const params = {
clusterCode: args.clusterCode.value,
excel: args.excel.value.buffer // 接受 buffer流表格
}
const xlsxHead = ['商品编号'] // 设置规定一个你想要的表头内容
let excel = null
try {
excel = xlsx.parse(params.excel) // 解析上传的表格内容 以行为单位
} catch (e) {
logger.error(e.stack)
logger.error(e.message)
return res.json({code: -1, message: 'excel解析错误,请确认是正确的excel文件'}).end()
}
if (!excel || _.isEmpty(excel[0].data)) {
return res.json({code: -1, message: '上传文件不合法,请重新上传!'}).end()
}
const theHead = excel[0].data[0] // 解析并取出表格的表头
logger.trace('excel表头', theHead)
if (!_.isEqual(theHead, xlsxHead)) { // 此处是为了判断并防止客服端随意乱吃不符合模板要求的表格。
return res.json({ code: -1, message: '该excel不符合格式要求,请下载模板填充数据!' }).end()
}
const rows = excel[0].data.slice(1) // 截取填入的数据部分 按照 行 截取
if (_.isEmpty(rows)) {
return res.json({code: -1, message: '您还未填完内容呢!'}).end()
}
// excel中 商品编号集合
let productionNos = rows.map(row => _.isEmpty(row) ? '' : row[0])
// 去除空行
productionNos = _.compact(productionNos)
logger.trace('上传的商品编号集合', productionNos)
const user = pickUser(req)
const ProductionModel = initProductionModel(user.customerDB)
const fn = co.wrap(function* () {
const products = yield ProductionModel._selectProductsNo(productionNos)
const selectProductionNos = products.map(item => Number(item.productionNo))
const errProductionNos = _.difference(productionNos, selectProductionNos) // 放入不合法的 商品编号
const errArr = errProductionNos.map(num => ({productionNo: num, errMsg: _.isNumber(num) ? '【商品编号】不存在!' : '【商品编号】不是数字!'}))
if (_.isEmpty(errArr)) {
const obj = {productionNos, clusterCodes:[params.clusterCode]}
logger.trace('上传商品加入集群参数', obj)
if(_.isEmpty(obj.productionNos)) return res.json({code: -1, message: '您填写的内容有误,请检查!'}).end()
return yield ProductionModel._insertProductionClusterMap(obj)
} else {
return Promise.reject(Utils.cError(-2, 'excel数据存在错误,请检查以下数据!', {products: errArr}))
}
})
fn()
.then(data => {
res.json({code: 0, message: '添加成功!', data}).end()
addLog(user.customerDB, user.userId, user.ip, 'TAGS', params.clusterCode, '已成功从集群中批量上传商品 Excel!')
})
.catch(err => {
return Utils.handleErr(res, err)
})
}
实际场景 2 为:客户端上传表格时,需要下载后台给出的模板表格。
exports.getExcel = function (args, req, res, next) {
/**
* getPromotionTaskExcel excel下载
**/
const params = {
campaignId: args.promotionCampaignId.value,
fileName: args.fileName.value || '任务表' // 这里可以自定义下载的文件名称
}
logger.trace('下载任务列表参数', params)
const user = pickUser(req)
const PromotionTaskModel = initializeTaskModel(user.customerDB)
const PromotionMaterialModel = initializeMaterialModel(user.customerDB)
const PromotionAllotMaterialModel = initializeAllotMaterialModel(user.customerDB)
const fn = co.wrap(function * () { // 将已经加入页面的数据添到要下载的表格中,再继续再表格中添加数据
const tasks = yield PromotionTaskModel._selectTaskList(params)
const materials = yield PromotionMaterialModel._selectMaterials(params)
const allotRecords = yield PromotionAllotMaterialModel.selectAllotRecord(params.campaignId)
// 按任务分组
const groupAllotRecords = _.groupBy(allotRecords, 'taskId')
// 组合数据
const thead = ['门店编号', '门店名称', '销售额(元)', '毛利(元)', '新增会员(人)']
materials.forEach(material => {
thead.push(material.materialName)
})
const tbody = tasks.map(task => {
const row = [task.organizationId, task.organizationName, task.sales, task.grossProfit, task.newCustomers]
const allotRecord = groupAllotRecords[task.taskId] || []
materials.forEach(material => {
const allot = _.find(allotRecord, ['materialId', material.materialId])
const count = allot ? allot.count : 0
row.push(count)
})
return row
})
// build excel buffer
tbody.unshift(thead)
let excelBuffer = Buffer.from('')
try {
excelBuffer = xlsx.build([{name: '任务表', data: tbody}])
fs.writeFileSync(cwd + `/public/download/temp/${params.fileName}.xlsx`, excelBuffer, {encoding: 'utf8'})
} catch (e) {
logger.error(e.stack)
logger.error(e.message)
return Promise.reject(cError(-1, 'Excel 创建失败,请稍候再试!'))
}
return {downloadLink: `/download/temp/${params.fileName}.xlsx`} // 返回下载相对服务器文件路径
})
fn()
.then(data => {
res.json({code: 0, message: '操作成功!', data}).end()
addLog(user.customerDB, user.userId, user.ip, 'PROMOTION', params.campaignId, '已成功下载营销活动任务列表(excel)')
})
.catch(e => handleErr(res, e))
}