MENU

Doc纯文本迁移到Doc表格中

September 25, 2020 • 浏览量: 489 • 字数: 4368 • 阅读时长: 3分钟 • 前端

前几日,一朋友给我发来了一个文档,说是让我帮忙把文本内容复制到一个新的表格内容中。当我做完第一份后,才知道还有很多文档需要处理。所以就想着做一个工具来批量处理。

示例

踩坑记录

起初是这样想的:

  • 先拿到文档的内容:因为给我的文档是.doc后缀的文件,然后通过一通readFile操作,发现读出来一堆文字乱码。索性先停掉了这部分工作。
  • 获取表格数据:最开始的想法是把表格转成HTML,然后通过设定模板的方法将内容导入。但是各种工具转成的HTML都不尽人意,没样式、文字乱码。

然后发现两条路都堵死了,那不行啊。文档实在是太多了,难道真要一个一个的去复制吗?

又找了很久,发现了一个原本忽视的内容:.docx文件,作为取代.doc的格式,他的本质是一个 zip 文件。

详细信息

docx 是微软 Word 的文件扩展名,Microsoft Office2007 之后版本使用,其基于 Office Open XML 标准的压缩文件格式取代了其以前专有的默认文件格式,在传统的文件名扩展名后面添加了字母“x”(即“.docx”取代“.doc”、“.xlsx”取代“.xls”、“.pptx”取代“.ppt”)。任何能够打开 DOC 文件的文字处理软件都可以将该文档转换为 DOCX 文件,docx 文件比 doc 文件所占用空间更小,docx格式的文件本质上是一个XML文件

docx格式的文件本质上是一个ZIP文件。将一个 docx 文件的后缀改为 ZIP 后是可以用解压工具打开或是解压的。事实上,Word2007 的基本文件就是 ZIP 格式的,他可以算作是 docx 文件的容器。

docx 格式文件的主要内容是保存为 XML 格式的,但文件并非直接保存于磁盘。它是保存在一个 ZIP 文件中,然后取扩展名为 docx。将 .docx 格式的文件后缀改为 ZIP 后解压, 可以看到解压出来的文件夹中有 word 这样一个文件夹,它包含了 Word 文档的大部分内容。而其中的document.xml文件则包含了文档的主要文本内容。
百度百科-docx

有了这个信息后,我觉得应该有希望了。第一步先被搁置了,我们从第二步开始。既然有了 xml 文件,那么就可以使用模板来进行 xml 的填充了。那就开始处理表格模板。

我们先把想要的字段都用标识字符进行占位(切记使用完整且准确的英文,不然会自动切割字符)

表格模板

有了模板以后,通过 npm 包adm-zip来直接解压表格模板数据。发现解压出来的document.xml里面已经包含了之前定义的标识占位符。

那么填充就显得很简单了。通过readFile读取到 xml 文件,然后替换的内容就完美填充到各个字段了。

到这里看起来后面的步骤已经完成了,但是第一步如何获取基础文档的内容呢。随后我在互联网的海洋中翻找了很久,找到了一个 npm 包@gmr-fms/word-extractor。可以直接读取到 doc 文档内容(在此感谢大佬)

通过@gmr-fms/word-extractor的支持,可以拿到整个文档,通过正则筛选出了我想要的字段内容,然后将内容替换给document.xml。完成内容的更改。

最后一步就是将第二步解压出来的文档文件再压缩成一个.docx文件,这里我使用的是archiver,可以直接将文件压缩为.docx

完美替换

代码实现

先装依赖

npm i @gmr-fms/word-extractor adm-zip archiver
const fs = require("fs");
const path = require("path");

// 读取doc文档工具
const extract = require("@gmr-fms/word-extractor");
// 直接解压docx文件
var admZip = require("adm-zip");
// 压缩文件
const archiver = require("archiver");

// 在doc文件夹下存放的是将要处理的文档
var files = fs.readdirSync(path.resolve(__dirname, "./doc"));

files.forEach((i) => {
  var fileName = i.split("_")[0];

  extract.fromFile(path.resolve(__dirname, "./doc/" + i)).then((doc) => {
    var body = doc.getBody();
    var number = body
      .match(/第.*单元/g)[0]
      .split("第")[1]
      .split("单元")[0];
    var name = body.match(/第.*课 .*/g)[0].split(" ")[1];
    var time = body
      .split("三、教学时间")[1]
      .split("四、教学过程")[0]
      .replace("\n", "")
      .replace("\n", "");
    var target = body.split("一、教学目标")[1].split("二、教学准备")[0].replace("\n", "");
    var have = body.split("二、教学准备")[1].split("三、教学时间")[0].replace("\n", "");
    var procedure = body.split("四、教学过程")[1].replace("\n", "");

    // template下存放的是表格模板文件  index.docx 为表格模板
    var zip = new admZip(`./template/index.docx`);

    // result文件夹存放的是解压出来的表格模板文件夹
    zip.extractAllTo(`./result/${fileName}`, true);
    var xml = fs.readFileSync(`./result/${fileName}/word/document.xml`); // 读取document.xml
    var xmlStr = xml.toString();
    // 开始替换文档
    xmlStr = xmlStr.replace(/number/g, number);
    xmlStr = xmlStr.replace(/name/g, name);
    xmlStr = xmlStr.replace(/time/g, time);
    xmlStr = xmlStr.replace(/target/g, target);
    xmlStr = xmlStr.replace(/have/g, have);
    xmlStr = xmlStr.replace(/procedure/g, procedure);
    // 重写文档
    fs.writeFileSync(`./result/${name}/word/document.xml`, xmlStr);

    var archive = archiver("zip", {
      zlib: {
        level: 8,
      },
    }).on("error", function (err) {
      throw err;
    });

    // 压缩到 zip 文件夹下的是最终生成的带表格的文档
    var output = fs.createWriteStream(__dirname + `/zip/${name}.docx`).on("close", function (err) {
      if (err) {
        console.log("关闭archiver异常:", err);
        return;
      }
      console.log(`已生成${name}文件`);
    });

    archive.pipe(output);
    archive.directory(`./result/${name}/`, "");
    archive.finalize();
  });
});

这里主要是提供了一个思路,供大家参考。合理的转变思路可能问题解决的会更方便点。

Archives QR Code Tip
QR Code for this page
Tipping QR Code