node+exceljs+stream流异步导出excel表格

第一次写博客有点慌

前段时间公司需要做个异步导出excel的功能

点击导出按钮后端返回数据前端通过node+exceljs把数据转为excel表格同时弹出弹框,支持翻页,弹框主要信息是一个列表,包括excel表名,如果导出完成了就有个下载表格,没有完成有个手动中断按钮,点击手动中断停止导出,添加导出失败,手动中断信息

刚开始用的是直接读取excel文件然后在里面写数据

核心伪代码如下

const filename = `xxx数据导出表${new Date().getTime()}.xlsx`;//表名
importPath = `${importPath}/kapi`;
const ws = await workbook.xlsx.readFile(path.join(importPath, fileName)).then(worksheet => {
    return worksheet._worksheets[1];
  }).catch(() => {
    const worksheet = workbook.addWorksheet('订单列表');
    worksheet.addRow(['表头','表头']);
    return worksheet;
  });
ws.addRow(['数据','数据'])

后来改成这样速度快了很多,基本上大部分代码如下

const path = require('path');
const fs = require('fs');
const axios = require('axios');
const Excel = require('exceljs');

let id=-1;//需要点击中断导出的id
// 设置生成excel文件所在的地址
importPath = `${importPath}/kapi`;
//往excel表里面添加数据
const getExcel = async ({list,getWs}) => {
  getWs(list);
  return {
    errcode: 0
  };
};

//翻页递归,直到最后一页
const getList = ({page,method,ctx,record,url,getWs}) => {
  const pageSize = 10;
  //调用数据列表接口返回相应数据
  return axios({
    method: method,
    url,
    timeout: 200 * 1000,
    params: Object.assign({}, ctx.request.body, {
      page: page,
      page_size: pageSize
    }),
    headers: {
      Cookie: ctx.headers.cookie,
      'X-Requested-With': 'XMLHttpRequest'
    }
  }).then(async res => {
      if (page == 1 && !res.data.total) {
        return {
          errcode: 1,
          message: '没有可导出的列表'
        };
      }
      //没调用一次就写一次表格
      let _res = await getExcel({
        list: res.data.data,
        getWs,
      });

      if (_res.errcode) return _res;
      //如果没有点击中断传给后端相应进度
        if(id!=record.id){
          const putRes = await axios({
            method: 'POST',
            url: `${ctx.headers.origin}/填写自己的后端接口`,
            data: {
                id:record.id,//导出表格的id
                status: Math.floor((res.data.total <= page * pageSize ? res.data.total : page * pageSize)/res.data.total),//状态0未完成1已完成  字段怎么取根据自己的接口来
                process: (res.data.total <= page * pageSize ? res.data.total : page * pageSize)/res.data.total*100,//进度0-100   字段怎么取根据自己的接口来
                msg:''//错误信息
            },
            headers: {
              Cookie: ctx.headers.cookie,
              'X-Requested-With': 'XMLHttpRequest'
            }
          }).then(() => ({
              errcode: 0
          })).catch(e => ({
              errcode: 1,
              message: e.message
          }));
          if (putRes.errcode) return putRes;
        }
      //判断是否递归完成,或者是否中断
      if (res.data.total <= page * pageSize||id==record.id) {
        return {
          errcode: 0
        };
      } else {//没完成而且没有中断的继续递归
        return getList({
          page: page + 1,
          ctx,
          method,
          record,
          url,
          getWs,
        });
    }

  }).catch(e => {
    return {
      errcode: 1,
      message: e.message
    };
  });
};


const Api = {
  //导出excel node接口
  info: async (ctx, next) => {
    const filename = `xxx数据导出表${new Date().getTime()}.xlsx`;//表名
    const row=['姓名','id','地址']//表头
    const ws = new Excel.stream.xlsx.WorkbookWriter({filename: path.join(importPath, filename)})//路径
    const worksheet = ws.addWorksheet('sheet1');//sheet表名
    worksheet.addRow(row).commit();//不管是表头或者数据每次添加都需要commit

    //每点击导出添加一条记录
    const postRes = await axios({
        method: 'post',
        url: `${ctx.headers.origin}/填写自己的后端接口(添加表格的接口)`,
        data: {
            type: 2,
            name:filename,
        },
        headers: {
          Cookie: ctx.headers.cookie,
          'X-Requested-With': 'XMLHttpRequest'
        }
    }).then(res => ({
        errcode: 0,
        data: res.data
    })).catch(e => ({
        errcode: 1,
        message: e.message
    }));

    if (postRes.errcode) {
        ctx.body = postRes;
        return;
    }
    getList({ 
      page: 1,
      method: 'POST',
      ctx,
      record: {fileName:filename,id:postRes.data.data.id},
      url: `${ctx.headers.origin}/填写自己的后端接口(需要导出的列表接口)`,
      getWs: (list) => {
        list.forEach(order => {
          worksheet.addRow([
            //这里随便写的,根据实际接口返回的数据和excel表格所需的字段来
            order.mp_name,
            order.id,
            order.name,
            order.address,
            //......
            
          ]).commit();//不管是表头或者数据每次添加都需要commit
        });
      }
    }).then(res => {
      ws.commit()//每次表格数据添加完成都需要commit这是必须的否则excel表格打不开
    }).catch(e => {
      throw new Error(e);
    });

    ctx.body = {
      errcode: 0
    };

  },
  //手动中断接口
  stopExport: async (ctx, next) => {
    id=ctx.request.body.id
    ctx.body = await axios({
      method: 'POST',
      url: `${ctx.headers.origin}/填写自己的后端接口`,
      data: {
        id:id,
        status: 2,
        msg: '手动中断'
      },
      headers: {
        Cookie: ctx.headers.cookie,
        'X-Requested-With': 'XMLHttpRequest'
      }
    });
    ctx.body = {
      errcode: 0
    };
    await next();
  },
};

module.exports = Api;


初来乍到还望大佬多多指教

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值