【Node.js】基于 Koa 将 Xlsx 文件转化为数据

背景

最近做需求的时候遇到了多语言渲染页面的场景

产品给的是一个 excel 文件,如果将 excel 文件手动录入到代码中比较费时间

本文就是针对这样的场景:基于 node-xlsx 库,实现了一个简单的 xlsx 文件处理工具


准备

数据准备

首先,我们需要准备一个 xlsx 文件作为上传数据的文件

搭建 Koa 项目

首先我们需要初始化一个 Koa 项目

1. 安装 koa-generator

shell 复制代码
npm install -g koa-generator

2. 使用 koa2 命令初始化项目

shell 复制代码
# 普通初始化
koa2 xlsx-handler

# 初始化并指定 ejs 作为模板引擎
koa2 -e xlsx-handler

安装对应的依赖

1. 安装 node-xlsx

shell 复制代码
npm install -S node-xlsx

2. 安装 @koa/multer 处理接口中间件

shell 复制代码
npm install -S @koa/multer multer

对 koa2 项目进行改造

根据 MVC 思想,我们需要将原本的文件逻辑进行拆分

shell 复制代码
app
├── bin
│   └── www
├── controllers
│   └── IndexController.js
│   └── XlsxController.js
├── routes
│   └── index.js
│   └── xlsx.js
├── views
│   └── common
│   └── index.ejs
├── public
│   └── css
│   └── js
│   └── img
├── app.js
├── package.json

实现

1. 接口定义

routes/xlsx.js 中定义接口

js 复制代码
const Router = require('koa-router');
const router = new Router();

const upload = require('@koa/multer')();

const xlsxController = require('../controllers/XlsxController');

router.prefix('/xlsx');

router.post('/upload', upload.fields([{ name: 'file', maxCount: 1 }]), xlsxController.upload);

module.exports = router;

2. 定义控制器

js 复制代码
// controllers/XlsxController.js

const jsonDTO = require('../entities/json');
const { parseXlsxToArray } = require('../libs/xlsx');

exports.upload = async (ctx, next) => {
  const file = ctx.files.file?.[0] ?? ctx.files.file;
  const data = parseXlsxToArray(file);
  ctx.body = jsonDTO.success(data);
};

3. 核心方法实现

js 复制代码
const nodeXlsx = require('node-xlsx');

exports.parseXlsxToArray = (file) => {
  const parsetXlsxResult = exports.parseXlsx(file);

  return Object.keys(parsetXlsxResult).reduce((res, key) => {
    res.push({
      sheetName: key,
      data: Object.keys(parsetXlsxResult[key]).map((subKey) => {
        // const langData = parsetXlsxResult[key][subKey];
        return {
          title: subKey,
          data: parsetXlsxResult[key][subKey],
        };
      }),
    });
    return res;
  }, []);
};

exports.parseXlsx = (file) => {
  const workSheetsFromBuffer = nodeXlsx.parse(file.buffer);

  return workSheetsFromBuffer.reduce((res, item, index) => {
    const { name: sheetName, data } = item;

    res[sheetName] = {};

    const [headers, ...rows] = data;

    headers
      .filter((_, i) => i > 1)
      .forEach((header, headerIndex) => {
        res[sheetName][header] = {};

        for (let row of rows) {
          const [key1, key2, ...values] = row;
          const rowKey = `${key1}.${key2}`;
          res[sheetName][header][rowKey] = values[headerIndex] ?? '';
        }
      });

    return res;
  }, {});
};

4. 统一响应体

js 复制代码
// entities/json.js

exports.success = (data) => {
  return {
    code: 0,
    message: 'ok',
    data,
  };
};

/**
 * @typedef {{ code: number, message: string, data: any }} ResponseBody
 * 失败响应
 * @type {(() => ResponseBody) | ((message: string) => ResponseBody) | ((code: number, message: string) => ResponseBody) | (<T> (code: number, message: string, data: T) => ResponseBody)}
 * @param {*} args 
 * @returns 
 */
exports.fail = (...args) => {
  if (args.length === 0) {
    return {
      code: 1,
      message: 'fail',
      data: null,
    }
  }
  if (args.length === 1) {
    return {
      code: 1,
      message: args[0],
      data: null,
    }
  }
  if (args.length === 2) {
    return {
      code: args[0],
      message: args[1],
      data: null,
    }
  }
  if (args.length === 3) {
    return {
      code: args[0],
      message: args[1],
      data: args[2],
    }
  }
};

5. 启动服务 & 测试接口

shell 复制代码
npm run dev

测试接口

使用 Postman 或其他工具测试接口

  • 接口地址:http://localhost:3000/xlsx/upload
  • 请求方法:POST
  • 上传文件:选择 xlsx 文件
  • 响应体:根据 entities/json.js 中的定义
http 复制代码
### 请求上传信息
POST http://localhost:3000/xlsx/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="locale.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

< ./locale.xlsx
------WebKitFormBoundary7MA4YWxkTrZu0gW--

响应数据示例

json 复制代码
{
    "code": 0,
    "message": "ok",
    "data": [
        {
            "sheetName": "当前显示的语言",
            "data": [
                {
                    "title": "简体中文/zh-CN",
                    "data": {
                        "USER_MANAGEMENT.USER_STATUS": "用户状态",
                        "USER_MANAGEMENT.DELETE_USER_SUCCESS": "删除用户提交成功",
                        "USER_MANAGEMENT.TIMESPAN": "自定义存期",
                        "USER_MANAGEMENT.EMAIL": "邮箱",
                        "USER_MANAGEMENT.RATE": "利率",
                        "USER_MANAGEMENT.FIXED_DATE": "固定存款期",
                        "USER_MANAGEMENT.FIXED_DATE_TYPE": "固定存期类型",
                        "USER_MANAGEMENT.AMOUNT": "存款额",
                        "USER_MANAGEMENT.CONTACT_TYPE": "存额类型",
                        "USER_MANAGEMENT.ENT_NAME": "客户名称",
                        "USER_MANAGEMENT.ACCOUNT_NO": "账户号码"
                    }
                },
                {
                    "title": "繁体中文/zh-XG",
                    "data": {
                        "USER_MANAGEMENT.USER_STATUS": "狀態",
                        "USER_MANAGEMENT.DELETE_USER_SUCCESS": "成功",
                        "USER_MANAGEMENT.TIMESPAN": "自定義存期",
                        "USER_MANAGEMENT.EMAIL": "郵箱",
                        "USER_MANAGEMENT.RATE": "利率",
                        "USER_MANAGEMENT.FIXED_DATE": "固定存款期",
                        "USER_MANAGEMENT.FIXED_DATE_TYPE": "固定存期類型",
                        "USER_MANAGEMENT.AMOUNT": "存款額",
                        "USER_MANAGEMENT.CONTACT_TYPE": "存額類型",
                        "USER_MANAGEMENT.ENT_NAME": "客戶名稱",
                        "USER_MANAGEMENT.ACCOUNT_NO": "賬戶號碼"
                    }
                },
                {
                    "title": "英文/en",
                    "data": {
                        "USER_MANAGEMENT.USER_STATUS": "state",
                        "USER_MANAGEMENT.DELETE_USER_SUCCESS": "success",
                        "USER_MANAGEMENT.TIMESPAN": "Custom Deposit Period",
                        "USER_MANAGEMENT.EMAIL": "Mail",
                        "USER_MANAGEMENT.RATE": "interest rate",
                        "USER_MANAGEMENT.FIXED_DATE": "Fixed deposit period",
                        "USER_MANAGEMENT.FIXED_DATE_TYPE": "Fixed Deposit Type",
                        "USER_MANAGEMENT.AMOUNT": "Deposit amount",
                        "USER_MANAGEMENT.CONTACT_TYPE": "Deposit type",
                        "USER_MANAGEMENT.ENT_NAME": "client's name",
                        "USER_MANAGEMENT.ACCOUNT_NO": "account number"
                    }
                }
            ]
        }
    ]
}
相关推荐
拾贰_C15 小时前
【node.js | Ubuntu | update】如何升级旧的nodejs本版至最新;如何升级npm
ubuntu·npm·node.js
湖边看客16 小时前
在 Windows PowerShell 里给 Node.js 设置内存上限
node.js
zhensherlock17 小时前
Protocol Launcher 系列:Beorg 高效任务管理的协议支持
前端·javascript·typescript·node.js·自动化·github·js
远洪1 天前
excel 找出两列不同的数据
excel
pcplayer1 天前
非常好用的 Excel 读写控件
excel·delphi·office
Navicat中国1 天前
使用 Navicat 导入向导导入 Excel 数据时,系统提示导入成功,表中也能看到数据,但行数统计显示为 0,这是什么原因?
数据库·excel·导入
shao9185161 天前
第3章(2)——使用Gradio JavaScript Client
javascript·node.js·cdn·gradio·job·events·playcode
穿着内裤的外星人1 天前
触控精灵远程读写Excel步骤配置
excel
Rabbit_QL2 天前
npm 不是“前端的包管理器“—它是 Node.js 的
前端·npm·node.js
是大强2 天前
nvm安装node成功npm失败
前端·npm·node.js