不想麻烦,就学会用 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 图标是具有版权问题的,所以一定程度上是需要进行保密的。

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

相关推荐
伍哥的传说6 分钟前
CSS+JavaScript 禁用浏览器复制功能的几种方法
前端·javascript·css·vue.js·vue·css3·禁用浏览器复制
lichenyang45313 分钟前
Axios封装以及添加拦截器
前端·javascript·react.js·typescript
Trust yourself24328 分钟前
想把一个easyui的表格<th>改成下拉怎么做
前端·深度学习·easyui
苹果醋329 分钟前
iview中实现点击表格单元格完成编辑和查看(span和input切换)
运维·vue.js·spring boot·nginx·课程设计
武昌库里写JAVA31 分钟前
iView Table组件二次封装
vue.js·spring boot·毕业设计·layui·课程设计
三口吃掉你33 分钟前
Web服务器(Tomcat、项目部署)
服务器·前端·tomcat
Trust yourself24335 分钟前
在easyui中如何设置自带的弹窗,有输入框
前端·javascript·easyui
烛阴39 分钟前
Tile Pattern
前端·webgl
前端工作日常1 小时前
前端基建的幸存者偏差
前端·vue.js·前端框架
Electrolux1 小时前
你敢信,不会点算法没准你赛尔号都玩不明白
前端·后端·算法