背景
最近做需求的时候遇到了多语言渲染页面的场景
产品给的是一个 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"
}
}
]
}
]
}