实现读取excel文件内容小功能【玩转前端】

举报
叶一一 发表于 2023/02/15 19:35:52 2023/02/15
【摘要】 分享一下读取excel文件内容小功能是如何实现的。

前言

我们在做业务需求的时候,会遇到展示表格的页面,如果只有一个表格,我们可以直接把内容填充到我们的代码中,有时候业务需要不同的分类展示不同的表格,而表格内容是静态的,这类需求,我之前的处理是写个本地json文件,每次新增让产品直接把数据录到json文件中。有时候业务会把Excel表格发给我,我自己进行录入。闲暇的时候我在想,可否通过解析Excel文件直接拿到内容呢?


于是我进行了后面的尝试。


Excel文件的读取

目标功能

我想实现的功能主要有几部分:读取Excel文件,识别多个工作表,每个工作表按行将数据放入数组中。最终解析输入如下:

Excel文件数据

两个工作表:鞋子和裤子

image.jpeg


image.jpeg

读取的完整数据

{
    "fileName": "测试读取.xlsx",
    "list": [
        {
            "sheetName": "鞋子",
            "list": [
                {
                    "goodName": "鞋子A版",
                    "size": "37",
                    "color": "白色"
                },
                {
                    "goodName": "鞋子B版",
                    "size": "38",
                    "color": "米色"
                },
                {
                    "goodName": "鞋子C版",
                    "size": "38",
                    "color": "黑色"
                }
            ]
        },
        {
            "sheetName": "裤子",
            "list": [
                {
                    "goodName": "裤子A版",
                    "size": "S",
                    "color": "白色"
                },
                {
                    "goodName": "裤子B版",
                    "size": "M",
                    "color": "黑色"
                },
                {
                    "goodName": "裤子C版",
                    "size": "L",
                    "color": "红色"
                },
                {
                    "goodName": "裤子D版",
                    "size": "XL",
                    "color": "紫色"
                }
            ]
        }
    ]
}


下面我将功能进行细分。

读取Excel文件

解析插件

插件:xlsx.core.min.js

官网:https://github.com/sheetjs/sheetjs


读取方法

FileReader可以读取Excel文件内容,它的详细知识点可以查看MDN

我这里使用的FileReader.readAsBinaryString(file) :开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。


HTML部分

包括文件上传和数据内容展示两部分,其中文件上传有限制文件类型必须是.xls或.xlsx后缀的。

<!DOCTYPE html>
<html>
  <head>
    <title>读取excel文件解析内容</title>
    <script src="https://cdn.bootcss.com/xlsx/0.11.5/xlsx.core.min.js"></script>
    <style>
      .warp {
        width: 100%;
        display: flex;
        justify-content: flex-start;
        margin-top: 10px;
      }
      span {
        display: block;
      }
      textarea {
        width: 80%;
        display: block;
      }
      .file {
        margin-bottom: 15px;
      }
      .tip {
        margin-bottom: 15px;
      }
    </style>
  </head>
  <body>
    <h3 class="tip">目前只能解析简单的纵向列表数据,支持多个工作表</h3>
    <div class="file">
      <input type="file" accept=".xls,.xlsx" onchange="uploadFile(this)" id="myFile" />
    </div>
    <div class="warp">
      <span>内容展示:</span>
      <textarea id="content" rows="30"></textarea>
    </div>
  </body>
</html>

上传操作方法

先判断上传文件是否为空,如果不为空才进行下一步。

/**
 * 文件上传操作
 * @param {Element} obj 选择的文件元素
 * @return {void} 无
 */
function uploadFile(obj) {
  //导入
  if (!obj.files) {
    return;
  }
  let f = obj.files[0];
  readExcelFile(obj, f);
}


文件读取

XLSX为sheetjs插件提供的方法

  • XLSX.read(data, read_opts):尝试解析data
  • workbook.SheetNames 是工作簿中工作表的有序列表;
  • wb.Sheets[sheetname] 返回一个代表工作表的对象;
  • XLSX.utils.sheet_to_json生成不同类型的 JS 对象。
/** @name 读取完成的数据 */
  var jsonContent;
  
/**
 * Excel文件处理方法
 * @param {Element} obj 选择的文件元素
 * @param {File} f 文件对象
 * @return {void} 无
 */
function readExcelFile(obj, f) {
  var reader = new FileReader();
  reader.onload = function (e) {
    var data = e.target.result;
    jsonContent = XLSX.read(data, {
      type: 'binary',
    });
    /** @name 工作簿中工作表的有序列表 */
    const sheetNamesList = jsonContent.SheetNames;
    const sheetsList = [];
    sheetNamesList.map(sheetItem => {
      //jsonContent.Sheets[Sheet名]获取该Sheet的数据
      const sheetItemData = jsonContent.Sheets[sheetItem];
      sheetsList.push({
        sheetName: sheetItem,
        list: XLSX.utils.sheet_to_json(sheetItemData), // 数据转成json格式
      });
    });
    pageShow(sheetsList, f);
  };
  reader.readAsBinaryString(f);
}


重组Excel文件数据

  • 现在的数据key为工作表表头的值,是中文名称,可以根据定义的变量名进行数据重组,所以我加了一个变量枚举renameObj。注:如果多个工作表的表头不一致,可重置renameObj对象,并改造resetTableData方法内的代码即可;
  • resetTableData方法会将数据的所有key替换成枚举中的变量值;
  • 变量arrayData为最终的处理之后的Excel数据,这时已经将Excel文件中的数据处理成了方便前端在页面回显的数据。
/**
 * 重组key-value
 * @param {Array} list 需要操作的数组
 * @param {Object} renameObj 重命名变量对象
 * @return {Array} 合并成的新数组
 */
function resetTableData(list, renameObj) {
  let newList = [].concat(list);
  newList.map(item => {
    for (let k in item) {
      const renameKey = renameObj[k];
      item[renameKey] = item[k];
      delete item[k];
    }
  });
  return newList; // 最终输出
}

/**
 * 数据回显
 * @param {Array} list 数据数组对象
 * @param {File} f 文件对象
 * @return {void} 无
 */
function pageShow(list, f) {
  let jsonData = '';
  
  /** @name 列表展示的变量名枚举 */
  const renameObj = {
    商品名: 'goodName',
    大小: 'size',
    颜色: 'color',
  };
  let newList = [].concat(list);
  newList.map(item => {
    item.list = resetTableData(item.list, renameObj);
  });
  /** @name 最终的数据 */
  let arrayData = {
    fileName: f.name,
    list: [].concat(newList),
  };
  const content = document.getElementById('content');
  jsonData = JSON.stringify(arrayData, null, 4);
  content.innerHTML = jsonData;
}


浏览器展示

image.jpeg

完整代码

<!DOCTYPE html>
<html>
  <head>
    <title>读取excel文件解析内容</title>
    <script src="https://cdn.bootcss.com/xlsx/0.11.5/xlsx.core.min.js"></script>
    <style>
      .warp {
        width: 100%;
        display: flex;
        justify-content: flex-start;
        margin-top: 10px;
      }
      span {
        display: block;
      }
      textarea {
        width: 80%;
        display: block;
      }
      .file {
        margin-bottom: 15px;
      }
      .tip {
        margin-bottom: 15px;
      }
    </style>
  </head>
  <body>
    <h3 class="tip">目前只能解析简单的纵向列表数据,支持多个工作表</h3>
    <div class="file">
      <input type="file" accept=".xls,.xlsx" onchange="uploadFile(this)" id="myFile" />
    </div>
    <div class="warp">
      <span>内容展示:</span>
      <textarea id="content" rows="30"></textarea>
    </div>
  </body>

  <script type="text/javascript">
    /**
     *FileReader读取方法:readAsBinaryString(file):开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。
     *@return {void} 无
     **/

    /** @name 读取完成的数据 */
    var jsonContent;

    /**
     * 文件上传操作
     * @param {Element} obj 选择的文件元素
     * @return {void} 无
     */
    function uploadFile(obj) {
      //导入
      if (!obj.files) {
        return;
      }
      let f = obj.files[0];
      readExcelFile(obj, f);
    }

    /**
     * Excel文件处理方法
     * @param {Element} obj 选择的文件元素
     * @param {File} f 文件对象
     * @return {void} 无
     */
    function readExcelFile(obj, f) {
      var reader = new FileReader();
      reader.onload = function (e) {
        var data = e.target.result;
        jsonContent = XLSX.read(data, {
          type: 'binary',
        });
        /** @name 工作簿中工作表的有序列表 */
        const sheetNamesList = jsonContent.SheetNames;
        const sheetsList = [];
        sheetNamesList.map(sheetItem => {
          //jsonContent.Sheets[Sheet名]获取该Sheet的数据
          const sheetItemData = jsonContent.Sheets[sheetItem];
          sheetsList.push({
            sheetName: sheetItem,
            list: XLSX.utils.sheet_to_json(sheetItemData), // 数据转成json格式
          });
        });
        pageShow(sheetsList, f);
      };
      reader.readAsBinaryString(f);
    }

    /**
     * 重组key-value
     * @param {Array} list 需要操作的数组
     * @param {Object} renameObj 重命名变量对象
     * @return {Array} 合并成的新数组
     */
    function resetTableData(list, renameObj) {
      let newList = [].concat(list);
      newList.map(item => {
        for (let k in item) {
          const renameKey = renameObj[k];
          item[renameKey] = item[k];
          delete item[k];
        }
      });
      return newList; // 最终输出
    }

    /**
     * 数据回显
     * @param {Array} list 数据数组对象
     * @param {File} f 文件对象
     * @return {void} 无
     */
    function pageShow(list, f) {
      let jsonData = '';
      /** @name 列表展示的变量名枚举 */
      const renameObj = {
        商品名: 'goodName',
        大小: 'size',
        颜色: 'color',
      };
      let newList = [].concat(list);
      newList.map(item => {
        item.list = resetTableData(item.list, renameObj);
      });
      /** @name 最终的数据 */
      let arrayData = {
        fileName: f.name,
        list: [].concat(newList),
      };
      const content = document.getElementById('content');
      jsonData = JSON.stringify(arrayData, null, 4);
      content.innerHTML = jsonData;
    }
  </script>
</html>


总结

日常开发中,如果觉得哪些不方便、不顺手的,不妨写个小工具协助开发,避免简单的重复的工作占用我们过多的时间。

作者:非职业「传道授业解惑」的开发者叶一一
简介:「趣学前端」、「CSS畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。