不想麻烦,就学会用 node 处理你的 excel 文件!

前言

这天闲聊之时收到小伙伴的吐槽,产品给了他一个 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 图标是具有版权问题的,所以一定程度上是需要进行保密的。

好了,本文到此结束,希望本文对你有所帮助!!!

相关推荐
GISer_Jing3 分钟前
前端算法实战:大小堆原理与应用详解(React中优先队列实现|求前K个最大数/高频元素)
前端·算法·react.js
武昌库里写JAVA1 小时前
SpringCloud
vue.js·spring boot·毕业设计·layui·课程设计
写代码的小王吧2 小时前
【安全】Web渗透测试(全流程)_渗透测试学习流程图
linux·前端·网络·学习·安全·网络安全·ssh
小小小小宇2 小时前
CSS 渐变色
前端
snow@li3 小时前
前端:开源软件镜像站 / 清华大学开源软件镜像站 / 阿里云 / 网易 / 搜狐
前端·开源软件镜像站
小小小小宇3 小时前
配置 Gemini Code Assist 插件
前端
one 大白(●—●)3 小时前
前端用用jsonp的方式解决跨域问题
前端·jsonp跨域
刺客-Andy3 小时前
前端加密方式 AES对称加密 RSA非对称加密 以及 MD5哈希算法详解
前端·javascript·算法·哈希算法
记得早睡~3 小时前
leetcode122-买卖股票的最佳时机II
javascript·数据结构·算法·leetcode
前端开发张小七4 小时前
13.Python Socket服务端开发指南
前端·python