前言
这天闲聊之时收到小伙伴的吐槽,产品给了他一个 excel 表格,里面包含了不同司航的基本信息和对应 logo 图标,内容大致如下(已经过脱敏处理
):

表格有效数据条数约 1000+ 条,现在需要前端方面在根据接口返回的 司航代码 去展示不同的 图标。
这个需求其实也没啥大毛病,只需要每个 图标列
和 代码列
实现关联即可,最直接的方式就是把对应图标使用对应的司航代码来命名即可,例如第一个 logo
可以命名为 A1.png
。

那么现在的问题是 1000+ 条数据需要 手动一个个保存图标,再挨个重命名进行关联,这个属实是个考验人耐心的工作。
于是我说道,其实这些繁琐的事情完全可以交给 node 来处理,无非就是需要写个小工具,主打的就是 一次实现,永久使用
,量还是那个量,但时间已经不是那个时间了。
他转头嘿嘿一笑,我就知道完了,果不其然蹦出了那句:哥,要不你来写一个吧!

如何从 Excel 中获取所有图片?
在之前的文章 《如何处理项目中的i18n文案和滚动失效的问题?》中,已经提到过如何提取 Excel 的数据,这里就不再额外赘述了。
现在我们需要考虑的问题是,要怎么实现从 Excel 中把所有图片获取出来呢?

其实没那么复杂,你只需要简单的搜索一下相关内容就会发现方式很多而且还很简单,下面就举两个例子。
将 excel 文件另存为 网页(*.htm, *.html)
正如标题一样,你只需要打开这个 excel 文件然后选择另存为 网页(*.htm, *.html)
格式,具体如下:

这样你就会得到如下的文件和文件夹:

打开 test.files
文件夹你会发现,里面包含了所有的文件内容,包括图片:

但是缺点也非常的明显:
- 内容比较混杂,不止包含图片内容,还有其他的文件
- 提取出的图可能会 粘连,也就是几张图片被连在一起,作为一张图片输出
将 excel 文件后缀改为 .zip
或 .rar
第二种方式就是直接更改 excel 文件后缀改为 .zip
或 .rar
:

这需要注意,不是把源文件进行压缩哈,对源文件进行压缩和直接更改后缀名得到的结果是不同的。
更改后缀完成之后,在打开这个文件:

你会发现在 xx.zip/xl/media 的目录中就是我们需要的图片集合,当然你使用 .rar
后缀也是一样的。
这种方式优势非常明显:
- 内容统一,全都是图片资源
- 图片自动 按顺序进行命名
显然,这种是我们当前需要的方式。

编写你的 node 工具
前面我们解决了从 excel 文件按顺序提取所有图片的问题,现在需要把这个解决思路变成 node 代码,同时还要处理 图片 和 司航代码 的关联问题。
实现 xlsx2img,提取图片合集
有了前面的思路,这里的实现就变得简单了:
-
将源文件进行复制得到副本,然后将副本文件后缀名更改为
.zip
或.rar
- 在 node 中只需要一个 fs.copyFile() 方法就能实现了
-
将更改后的
xxx.zip
文件进行解压- 这里我们使用第三方库 adm-zip 来实现就好,体积只有 121 kB
-
从解压的文件目录中,获取 [xxx]/xl/media 目录,将该目录拷贝到指定位置即可
- 在 node 中只需要一个 fs.cp() 方法就能实现了
-
最后一步,我们需要把临时文件进行清除即可
- 在 node 中使用 fs.unlinkSync() 删除文件,使用 fs.rmSync() 删除指定目录即可
完整代码如下:
js
const path = require("path");
const fs = require("fs");
const AdmZip = require("adm-zip");
const { ensureDirExistence } = require("../../utlis");
function xlsx2img({ input, output }) {
// 1.重命名 xlsx 文件为 zip 文件
const zipPath = path.join(__dirname, "copy.zip");
fs.copyFileSync(input, zipPath);
// 2.解压文件
const admzip = new AdmZip(zipPath);
const extractPath = path.join(__dirname, "extract");
admzip.extractAllTo(extractPath, true); // 第二个参数表示需要覆盖
// 4.将图片文件夹提取出来:/xl/media
ensureDirExistence(output); // 确保目录存在
fs.cpSync(path.join(extractPath, "./xl/media"), output, { recursive: true });
// 5. 删除临时文件
fs.unlinkSync(zipPath);
fs.rmSync(extractPath, { recursive: true });
}
module.exports = {
xlsx2img,
};
演示效果如下:

实现 xlsx2josn,提取数据集合
想要完成 图片 和 数据 的关联性,前面拿到了图片集合,那么现在还差数据集合,因此还需解析一下 excel 文件中的数据内容,那这里就使用一个库 `xlsx-to-json 来实现功能即可,只需要再简单封装下。
大多时候我们需要的并不是从 excel 直接生成的 json 数据,因为这样转换的格式是不符合的,例如直接转换后的结果:

这个时候我们就可以在下面定义的 callback 回调中进行数据格式化,把目标数据重新写回对应的 json 文件即可,在 node 中你可以使用 fs.writeFileSync('xxx.json', JSON.stringify(data))
来实现目的。
完整代码如下:
js
const xlsx2json = require("xlsx-to-json");
const path = require("path");
const fs = require("fs");
function xlsxToJson(
option = {},
callback
) {
let { input, output } = option;
let removeDefaultFile = false;
// 不传入 output 需要生成默认的输出路径
if (!output) {
const inputPaths = input.split("/");
const fileName = inputPaths.pop().split(".")[0];
output = path.join(inputPaths.join("/"), `${fileName}.json`);
}
xlsx2json(
{
input,
output,
},
function (err, result) {
if (err) {
console.error(err);
return;
}
callback && callback(result, { input, output });
}
);
}
module.exports = { xlsxToJson };
演示效果如下:

实现 dataMappingFile,完成数据关联
这里实际上就只有两步:
按顺序
读取 images 目录下的图片- node 中可以直接通过 fs.readdir() 读取对应目录下的所有文件
- 依次对读取到的图片名称
重命名为对应的司航代码
- node 中可以通过 fs.rename() 对目标文件进行重命名
这里看着还是很简单的,但是有些点还是需要考虑一下,避免踩坑:
- 使用 fs.readdir() 读取目录下所有文件时,得到的结果 并不是期望的顺序
- 很明显这个结果是按 UTF-16 码元值升序排序 的结果,不是我们预期的结果,但是我们可以重新将它们按数字顺序进行排序

完整代码如下:
js
const fs = require("fs");
const { ensureDirExistence } = require("../../utlis");
// 根据数据进行文件映射
function dataMappingFile({ inputDir, outDir, data, dataKey }) {
// 读取目录下的所有文件
fs.readdir(inputDir, (err, files) => {
// 按文件名中的数字排序
files = files
.map((fileName) => {
return {
index: parseInt(fileName),
fileName,
};
})
.sort((a, b) => a.index - b.index);
// 重命名文件
const tasks = files.map(({ fileName }, index) => {
const ext = fileName.split(".").pop();
const oldPath = `${inputDir}/${fileName}`;
const newPath = `${outDir}/${data[index][dataKey]}.${ext}`;
// 确保目录存在
ensureDirExistence(outDir);
return new Promise((resolve, reject) => {
fs.rename(oldPath, newPath, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
// 批量执行重命名操作
Promise.allSettled(tasks).then((results) => {
const rejectedItem = results.find((v) => v.status === "rejected");
if (rejectedItem) {
throw rejectedItem.reason;
} else {
console.log(`
==================================================================================================
数据和文件关联成功,请查看【${outDir}】下的文件
==================================================================================================
`);
}
});
});
}
module.exports = {
dataMappingFile,
};
演示效果如下:

最后
欢迎关注同名公众号《熊的猫》,文章会同步进行更新!
本文以从 Excel 中导出所有图片 、Excel 中导出数据集 、图片集合与数据集相关联 为例子来分享如何使用 node 来解决一些繁杂的工作,从而达到释放双手的目的,当然如果你需要的是把 数据、图片写入 Excel ,你可以使用 ExcelJS 来实现。
其实也有很多线上的工具供我们使用,但某些 Excel 文件中的数据是内部(或者是合作方)的数据,这些是不能轻易上传到某些站点的,例如 文中示例的 logo 图标是具有版权问题的
,所以一定程度上是需要进行保密的。
好了,本文到此结束,希望本文对你有所帮助!!!
