乐趣区

关于node.js:Node-系列-007-nodexlsx

——————————☆☆☆——————————

Node 系列相应地址:

  • 代码仓库:https://github.com/LiangJunro…
  • 文章仓库:https://github.com/LiangJunro… 系列 - 前端材料 /Node

——————————☆☆☆——————————

在通过 Puppeteer 操作浏览器下载到 Excel 之后,咱们终于能够将准备将多语言的操作玩出花来了。

本篇咱们将通过 node-xlsx,对 Excel 进行多语言导入导出的操作。

一 前言

在服务端的工作中,生成报表并送给经营、产品进行剖析应该是一门简略手艺。

然而在前端中,能这样耍的机会并不多,所以多语言操作是个好玩的点(没接触过的会感觉比拟陈腐)。

当然,既然服务端能够,对 Node.js 来说,提供这种性能也无可非议。

jsliang 十分懒,所以直奔主题关上 GitHub:

那就第 1 个了,不要搞什么调研不调研的,对于非生产数据来说,我就是玩~

看第一行简介:Excel file parser/builder that relies on js-xlsx.

js-xlsx?这个我晓得啊,在 2021.06.03 这一刻有 25.7k Star 的仓库地址:https://github.com/SheetJS/sheetjs

其实一开始试了下它对于 Node 的,enm… 一时半会没入门!

然而,我还是用我的 node-xlsx 吧,毕竟例子都在它仓库的 README.md 贴出来了!

二 疾速开始

  • 安装包:npm i node-xlsx -S
  • 装置 TypeScript:npm i @types/node-xlsx -D

2.1 测试导入

src/index.ts

import program from 'commander';
import common from './common';
import './base/console';
import xlsx from 'node-xlsx';
import fs from 'fs';

program
  .version('0.0.1')
  .description('工具库')

program
  .command('jsliang')
  .description('jsliang 帮忙指令')
  .action(() => {common();
  });

program
  .command('test')
  .description('测试频道')
  .action(async () => {
    // 测试新性能的时候应用
    
    // 以 buffer 模式导入
    const workSheetsFromBuffer = xlsx.parse(fs.readFileSync(`${__dirname}/common/dist/Excel 试用文件.xlsx`));
    console.log(JSON.stringify(workSheetsFromBuffer, null, 2));

    // 以文件模式导入
    const workSheetsFromFile = xlsx.parse(`${__dirname}/common/dist/Excel 试用文件.xlsx`);
    console.log(JSON.stringify(workSheetsFromFile, null, 2));
  });

program.parse(process.argv);

执行 npm run test,控制台打印如下:

---1---
[
  {
    "name": "Sheet1",
    "data": [
      [
        "key",
        "zh-CN",
        "en-US",
        "zh-TW",
        "zh-GZ"
      ],
      [
        "noMoney",
        "我没钱啦!",
        "I have no money",
        "我沒錢啦!",
        "我冇钱啦!"
      ]
    ]
  }
]

---2---
[
  {
    "name": "Sheet1",
    "data": [
      [
        "key",
        "zh-CN",
        "en-US",
        "zh-TW",
        "zh-GZ"
      ],
      [
        "noMoney",
        "我没钱啦!",
        "I have no money",
        "我沒錢啦!",
        "我冇钱啦!"
      ]
    ]
  }
]

OK,都能失常导入~

2.2 测试导出

import program from 'commander';
import common from './common';
import './base/console';
import xlsx from 'node-xlsx';
import fs from 'fs';

program
  .version('0.0.1')
  .description('工具库')

program
  .command('jsliang')
  .description('jsliang 帮忙指令')
  .action(() => {common();
  });

program
  .command('test')
  .description('测试频道')
  .action(async () => {
    // 测试新性能的时候应用
    
    // 导出数据
    const data = [[1, 2, 3],
      [true, false, null, 'sheetjs'],
      ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'],
      ['baz', null, 'qux'],
    ];
    const buffer = xlsx.build([{name: "jsliang", data: data}]); // 拿到文件 buffer

    // 写入文件
    fs.writeFileSync(`${__dirname}/common/dist/test-sheet.xlsx`, Buffer.from(buffer));
  });

program.parse(process.argv);

执行 npm run test 后,目录变成:

关上这个 Excel 文件,能够看到:

好的,导出也 OK 了~

2.3 测试定制宽度

当然,有时候产品十分懒,须要咱们将表格宽度给做好成每一列都能宽一点,那就要定制下页面宽度:

index.ts

import program from 'commander';
import common from './common';
import './base/console';
import xlsx from 'node-xlsx';
import fs from 'fs';

program
  .version('0.0.1')
  .description('工具库')

program
  .command('jsliang')
  .description('jsliang 帮忙指令')
  .action(() => {common();
  });

program
  .command('test')
  .description('测试频道')
  .action(async () => {
    // 测试新性能的时候应用
    
    // 导出数据
    const data = [['key', 'zh-CN', 'en-US', 'zh-TW', 'zh-GZ'],
      ['noMoney', '我没钱啦!', 'I have no money', '我沒錢啦!', '我冇钱啦!'],
    ];
    // 列宽设置
    const options = {
      '!cols': [{ wch: 10},
        {wch: 15},
        {wch: 15},
        {wch: 15},
        {wch: 15},
      ]
    }
    // 生成 buffer
    const buffer = xlsx.build([{name: "jsliang", data: data}], options); // 拿到文件 buffer

    // 写入文件
    fs.writeFileSync(`${__dirname}/common/dist/Excel 导出文件.xlsx`, Buffer.from(buffer));
  });

program.parse(process.argv);

执行 npm run test,看到 dist 目录生成:

而后点开「Excel 导出文件.xlsx」,外面内容为:

劳碌,满屏飘满 no money~

三 多语言操作

在咱们简略理解 node-xlsx 之后,咱们就能够通过它实现多语言的导入导出,以及下一章会解说如何获取须要的资源。

3.1 导入

接「006 – Puppeteer」,咱们在上一篇文章曾经实现了资源的下载,实际上咱们应该一条龙服务,从下载到导入通通给安顿了。

那么,咱们以后的目录须要革新一番:

- src
  + base
  - common
    - language
      + dist
      - download.ts
      - export.ts
      - import.ts
      - source.json
    - index.ts
    - questionList.ts
    - sortCatalog.ts
  - index.ts

文字目录如同没那么清晰,还是贴个图吧:

那么,开始写代码:

questionList.ts – 先明确本人的发问路线

// common 板块的问题征询路线
export const questionList = {
  '公共服务': { // q0
    '文件排序': { // q1
      '须要排序的文件夹': 'Work 工作', // q2
    },
  },
  '多语言': { // q0
    '下载多语言资源': { // q3
      '下载地址': 'Work 工作', // q4
    },
    '导入多语言资源': { // q3
      '下载地址': 'Work 工作', // q4
    },
    '导出多语言资源': { // q3
      '导出全量资源': 'Work 工作',
      '导出单门资源': 'Work 工作',
    }
  },
};

index.ts

import {inquirer} from '../base/inquirer';
import {Result} from '../base/interface';
import {sortCatalog} from './sortCatalog';
import {downLoadExcel} from './language/download';
import {importLanguage} from './language/import';
import {exportLanguage} from './language/export';

// 问题记录器
const answers = {
  q0: '',
  q1: '',
  q2: '',
  q3: '',
  q4: '',
};

const common = (): void => {
  // 问题路线:看 questionList.ts
  const questionList = [
    // q0
    {
      type: 'list',
      message: '请问须要什么服务?',
      choices: ['公共服务', '多语言']
    },
    // q1
    {
      type: 'list',
      message: '以后公共服务有:',
      choices: ['文件排序']
    },
    // q2
    {
      type: 'input',
      message: '须要排序的文件夹为?(绝对路径)',
    },
    // q3
    {
      type: 'list',
      message: '请问多语言须要什么反对?',
      choices: [
        '下载多语言资源',
        '导入多语言资源',
        '导出多语言资源',
      ],
    },
    // q4
    {
      type: 'input',
      message: '资源下载地址(HTTP)?',
      default: 'https://www.kdocs.cn/l/sdwvJUKBzkK2',
    }
  ];

  const answerList = [
    // q0 - 请问须要什么服务?async (result: Result, questions: any) => {
      answers.q0 = result.answer;
      switch (result.answer) {
        case '公共服务':
          questions[1]();
          break;
        case '多语言':
          questions[3]();
          break;
        default: break;
      }
    },
    // q1 - 以后公共服务有:async (result: Result, questions: any) => {
      answers.q1 = result.answer;
      if (result.answer === '文件排序') {questions[2]();}
    },
    // q2 - 须要排序的文件夹为?(绝对路径)async (result: Result, _questions: any, prompts: any) => {
      answers.q2 = result.answer;
      const sortResult = await sortCatalog(result.answer);
      if (sortResult) {console.log('排序胜利!');
        prompts.complete();}
    },
    // q3 - 请问多语言须要什么反对?async (result: Result, questions: any, prompts: any) => {
      answers.q3 = result.answer;
      switch (result.answer) {
        case '下载多语言资源':
        case '导入多语言资源':
          questions[4]();
          break;
        case '导出多语言资源':
          const exportResult = await exportLanguage();
          if (exportResult) {console.log('导出胜利!');
            prompts.complete();}
        default: break;
      }
    },
    // q4 - 资源下载地址(HTTP)?async (result: Result) => {
      answers.q4 = result.answer;
      const download = async (): Promise<any> => {const downloadResult = await downLoadExcel(result.answer);
        if (downloadResult) {console.log('下载胜利!');
          return true;
        }
      };
      switch (answers.q3) {
        case '下载多语言资源':
          await download();
          break;
        case '导入多语言资源':
          await download();
          const importResult = await importLanguage();
          if (importResult) {console.log('导入结束!');
          }
        default:
          break;
      }
    },
  ];

  inquirer(questionList, answerList);
};

export default common;

须要留神的是,咱们如果要导入的话,必定有个对应的资源文件,这边就用 source.json 演示:

source.json

{"zh-CN": {},
  "en-US": { },
  "zh-TW": { },
  "zh-GZ": {}}

简版内容如下,通过 import.ts 导入资源并填充外面内容:

import.ts

import xlsx from 'node-xlsx';
import fs from 'fs';
import path from 'path';

export const importLanguage = async (): Promise<boolean> => {const language = JSON.parse(fs.readFileSync(path.join(__dirname, './source.json'), 'utf8'));

  const workSheetsFromBuffer = xlsx.parse(
    fs.readFileSync(path.join(__dirname, '/dist/Excel 试用文件.xlsx'),
    ),
  );

  const sheet1Data = workSheetsFromBuffer[0].data.map(i => i.map(j => String(j)));

  // 获取头部数据
  const header = sheet1Data[0];
  
  // 查找 key 对应列
  let keyIndex = 0;
  for (let i = 0; i < header.length; i++) {if (header[i] === 'key') {
      keyIndex = i;
      break;
    }
  }
  if (keyIndex < 0) {console.error('未找到 key 对应列!');
    return false;
  }

  // 设置资源内容
  const fullLanguage: any[] = [...Object.keys(language), ...header.filter((item: any) => item !== 'key')];
  const filterFullLanguage = new Set();
  for (let i = 0; i < fullLanguage.length; i++) {if (!filterFullLanguage.has(fullLanguage[i])) {filterFullLanguage.add(fullLanguage[i]);
      // 如果没有该种语言,则新增
      if (!language[fullLanguage[i]]) {language[fullLanguage[i]] = {};}
    }
  }

  // 获取内容数据
  const body = sheet1Data.slice(1);
  for (let i = 0; i < body.length; i++) {for (let j = 0; j < body[i].length; j++) {if (j !== keyIndex) {const nowLanguage = language[header[j]]; // 一个损耗性能的操作,每次都会读取新列表,然而我不想优化
        const nowKey = body[i][keyIndex]; // 获取这一行的 key
        nowLanguage[nowKey] = body[i][j]; // 替换 key
      }
    }
  }

  fs.writeFileSync(path.join(__dirname, './source.json'), JSON.stringify(language, null, 2), 'utf8');

  return true;
};

export.ts

export const exportLanguage = async (): Promise<boolean> => {
  // 具体内容待补充
  return await true;
};

编写结束,执行 npm run jsliang,依照发问一一回车:

而后代码跑起来(姿态很帅),胜利导入:

这样导入流程就结束了。

当然,导入的过程中,还须要修复对齐 key(即某中文 key 状况下,其余资源未翻译;或者删除 key 资源),这些就不颤抖列举了,须要的时候补充写一写,也不难~

3.2 导出

导入尚且如此,导出就更轻松了:

export.ts

import xlsx from 'node-xlsx';
import fs from 'fs';
import path from 'path';

export const exportLanguage = async (): Promise<boolean> => {const languageData = JSON.parse(fs.readFileSync(path.join(__dirname, './source.json'), 'utf8'));

  // 组装头部数据
  const header = Object.keys(languageData);

  // 组装内容数据
  const chineseKeyList = Object.keys(languageData['zh-CN']);
  const body: any[] = [];
  for (let i = 0; i < chineseKeyList.length; i++) {const nowKey = chineseKeyList[i];
    const nowFloor = [nowKey];
    console.log(nowFloor, nowKey);
    for (let j = 0; j < header.length; j++) {const nowLanguage = header[j];
      nowFloor.push(languageData[nowLanguage][nowKey]);
    }
    body.push(nowFloor);
  }

  // 导出数据
  const data = [['keys', ...header],
    ...body,
  ];
  const buffer = xlsx.build([{name: "jsliang", data: data}]); // 拿到文件 buffer

  // 写入文件
  fs.writeFileSync(path.join(__dirname, './dist/Excel 导出文件.xlsx'), Buffer.from(buffer));

  return await true;
};

执行 npm run jsliang,按流程点点:

而后就看下 dist 目录有没有对应的文件:

在关上文件看看:

OK,搞定,出工~

四 后续

那么,Excel 的操作流程咱们就安顿得明明白白了。

再往下一章,jsliang 可能开启 Node 服务,实现简略网站的搭建,不过 jsliang 于 2018 年写过一篇 Node 从 0 根底到企业官网的文章了,所以咱们尝试搞个小游戏吧,嘿嘿~

以后阶段算上 Node 初篇结束,次要也没啥内容,后续会补充开启服务,WebSocket 等内容,冲鸭~

五 参考文献

  • nodejs 实现导出 excel 报表
  • GitHub:SheetJS
  • GitHub:node-xlsx

jsliang 的文档库由 梁峻荣 采纳 常识共享 署名 - 非商业性应用 - 雷同形式共享 4.0 国内 许可协定 进行许可。<br/> 基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/> 本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。

退出移动版