资讯专栏INFORMATION COLUMN

【实战教程】使用云函数将数据表导出为 Excel 文件

iOS122 / 3315人阅读

摘要:本文将介绍通过知晓云云函数来实现将数据表导出为文件的功能,并使用和将代码打包上传到知晓云。

在日常的工作中,常常需要根据运营需求对数据进行各种格式的处理和导出。导出后,不少人偏爱将数据放入 excel 在进行处理。

一般来说,处理数据导出时需要对数据进行一些运算整理。在以前,处理的方式是在一台独立的服务器上跑脚本。

而现在有了知晓云,不再需要维护服务器,直接写代码就能把相关事都都丢给云函数。 本文将介绍通过知晓云云函数来实现将数据表导出为 excel 文件的功能,并使用 webpackmincloud 将代码打包上传到知晓云。

技术栈:

打包工具:webpack@4.22.0

部署工具:mincloud@1.0.4

Excel 处理:node-xlsx@0.14.1

其他:知晓云 SDK

一、项目搭建

项目文件结构:

  export-excel-file
  ├── index.js
  ├── package.json
  ├── src
  │   └── index.js
  ├── webpack.config.js
  └── yarn.lock

项目搭建与云函数代码打包示例文档基本一致。项目搭建好后,还需要安装以下依赖(两种安装方式选其一即可):

// 使用 yarn 安装
yarn add node-xlsx mincloud
// 使用 npm 安装
npm install --save node-xlsx minclou

修改 deploy 脚本,如下:

// package.json
...
"scripts": {
   "build": "webpack --mode production",
   "predeploy": "npm run build",
   "deploy": "mincloud deploy export-excel-file ../"
},
...

最终我们会使用以下两个命令来部署和测试:

npm run deploy  // 部署到知晓云
mincloud invoke export-excel-file  // 测试已经部署到知晓云上的云函数
二、将数据表导出为 excel 文件

我们需要准备两张表:
order: 订单表 (新建字段:name, price)
export_task:导出任务记录表 (新建字段:file_download_link)

知晓云的云函数调用有同步和异步两种方式,同步调用的最大超时时间为 5 s,异步调用的则为 300 s。

假定 order 订单表有十万条数据,由于知晓云单次拉取数据的最大限制为 1000 条,所以需要分批获取数据,加上后续可能需要对数据进行处理,所花费的时间将会超过 5 s,因此对该云函数的调用将采用异步的方式。这时候就需要 export_task 导出任务记录表来对导出任务进行管理了。

export_task 表对导出任务进行管理的流程如下:

调用云函数时在 export_task 表中创建一条记录 A,此时记录 A 中的 file_download_link 字段值为空,同时拿到记录 A 的 id,记这个 id 为 jobId

进行 order 表数据查询,excel 文件生成,文件上传等操作,拿到文件下载链接

之后根据 jobId 来更新第一步创建的记录,保存文件下载链接到 file_download_link 字段中

更新完后就能在 export_task 表中拿到文件下载链接

通过上面的准备和分析,对导出 excel 文件操作分为以下 4 个步骤:

order 订单表数据获取

使用获取的数据在云函数环境下创建 excel 文件

将创建出的 excel 文件上传到知晓云

保存文件下载链接到 export_task 表中的 file_download_link 字段

完整代码如下:

const fs = require("fs")
const xlsx = require("node-xlsx")


const EXPORT_DATA_CATEGORY_ID = "5c711e3119111409cdabe6f2"    // 文件上传分类 id
const TABLE_ID = {
  order: 66666,         // 订单表
  export_task: 66667,   // 导出任务记录表
}


const TMP_FILE_NAME = "/tmp/result.xlsx"  // 本地临时文件路径,以 /tmp 开头,具体请查看:https://doc.minapp.com/support/technical-notes.html (云函数的临时文件存储)
const ROW_NAME = ["name", "price"]        // Excel 文件列名配置
const MAX_CONNECT_LIMIT = 5               // 最大同时请求数
const LIMIT = 1000                        // 单次最大拉取数据数
let result = []


/**
 * 更新导出记录中的 file_download_link 字段
 * @param {*} tableID 
 * @param {*} recordId 
 * @param {*} fileLink 
 */
function updateExportJobIdRecord(tableID, recordId, fileLink) {
  let Schame = new BaaS.TableObject(tableID)
  let schame = Schame.getWithoutData(recordId)


  schame.set("file_download_link", fileLink)
  return schame.update()
}


/**
 * 创建数据导出任务
 * 设置初始 file_download_link 为空
 * 待导出任务执行完毕后将文件下载地址存储到 file_download_link 字段中
 * @param {*} tableID 
 */
function createExportJobIdRecord(tableID) {
  let Schame = new BaaS.TableObject(tableID)
  let schame = Schame.create()
  return schame.set({file_download_link: ""}).save().then(res => {
    return res.data.id
  })
}


/**
 * 获取总数据条数
 * @tableId {*} tableId
 */
function getTotalCount(tableId) {
  const Order = new BaaS.TableObject(tableId)
  return Order.count()
    .then(num => {
      console.log("数据总条数:", num)
      return num
    })
    .catch(err => {
      console.log("获取数据总条数失败:", err)
      throw new Error(err)
    })
}


/**
 * 分批拉取数据
 * @param {*} tableId 
 * @param {*} offset 
 * @param {*} limit 
 */
function getDataByGroup(tableId, offset = 0, limit = LIMIT) {
  let Order = new BaaS.TableObject(tableId)
  return Order.limit(limit).offset(offset).find()
    .then(res => {
      return res.data.objects
    })
    .catch(err => {
      console.log("获取分组数据失败:", err)
      throw new Error(err)
    })
}


/**
 * 创建 Excel 导出文件
 * @param {*} sourceData 源数据
 */
function genExportFile(sourceData = []) {
  const resultArr = []
  const rowArr = []


  // 配置列名
  rowArr.push(ROW_NAME)


  sourceData.forEach(v => {
    rowArr.push(
      ROW_NAME.map(k => v[k])
    )
  })


  resultArr[0] = {
    data: rowArr,
    name: "sheet1",    // Excel 工作表名
  }


  const option = {"!cols": [{wch: 10}, {wch: 20}]}    // 自定义列宽度
  const buffer = xlsx.build(resultArr, option)
  return fs.writeFile(TMP_FILE_NAME, buffer, err => {
    if (err) {
      console.log("创建 Excel 导出文件失败")
      throw new Error(err)
    }
  })
}


/**
 * 上传文件
 */
function uploadFile() {
  let MyFile = new BaaS.File()
  return MyFile.upload(TMP_FILE_NAME, {category_id: EXPORT_DATA_CATEGORY_ID})
    .catch(err => {
      console.log("上传文件失败")
      throw new Error(err)
    })
}


module.exports = async function(event, callback) {
  try {
    const date = new Date().getTime()
    const groupInfoArr = []
    const groupInfoSplitArr = []
    const [jobId, totalCount] = await Promise.all([createExportJobIdRecord(TABLE_ID.export_task), getTotalCount(TABLE_ID.order)])
    const groupSize = Math.ceil(totalCount / LIMIT) || 1


    for (let i = 0; i < groupSize; i++) {
      groupInfoArr.push({
        offset: i * LIMIT,
        limit: LIMIT,
      })
    }


    console.log("groupInfoArr:", groupInfoArr)


    const length = Math.ceil(groupInfoArr.length / MAX_CONNECT_LIMIT)


    for (let i = 0; i < length; i++) {
      groupInfoSplitArr.push(groupInfoArr.splice(0, MAX_CONNECT_LIMIT))
    }


    console.log("groupInfoSplitArr:", groupInfoSplitArr)


    const date0 = new Date().getTime()
    console.log("处理分组情况耗时:", date0 - date, "ms")


    let num = 0


    // 分批获取数据
    const getSplitDataList = index => {
      return Promise.all(
        groupInfoSplitArr[index].map(v => {
          return getDataByGroup(TABLE_ID.order, v.offset, v.limit)
        })
      ).then(res => {
        ++num
        result.push(...Array.prototype.concat(...res))
        if (num < groupInfoSplitArr.length) {
          return getSplitDataList(num)
        } else {
          return result
        }
      })
    }


    Promise.all([getSplitDataList(num)]).then(res => {
      const date1 = new Date().getTime()
      console.log("结果条数:", result.length)
      console.log("分组拉取数据次数:", num)
      console.log("拉取数据耗时:", date1 - date0, "ms")


      genExportFile(result)


      const date2 = new Date().getTime()
      console.log("处理数据耗时:", date2 - date1, "ms")


      uploadFile().then(res => {
        const fileLink = res.data.file_link
        const date3 = new Date().getTime()
        console.log("上传文件耗时:", date3 - date2, "ms")
        console.log("总耗时:", date3 - date, "ms")


        updateExportJobIdRecord(TABLE_ID.export_task, jobId, fileLink)
          .then(() => {
            const date4 = new Date().getTime()
            console.log("保存文件下载地址耗时:", date4 - date3, "ms")
            console.log("总耗时:", date4 - date, "ms")


            callback(null, {
              message: "保存文件下载地址成功",
              fileLink,
            })
          })
          .catch(err => {
            callback(err)
          })
      }).catch(err => {
        console.log("上传文件失败:", err)
        throw new Error(err)
      })
    })
  } catch (err)
三、部署并测试

npm 一样,部署前需要先登录,请参照文档配置。

使用以下命令即可将云函数部署到知晓云:

npm run deploy

执行结果如下:

使用以下的命令来测试:

mincloud invoke export-excel-file

执行结果如下:

export_task 表记录:

上传到知晓云的 excel 文件如下:

文件内容:

四、参考文档

知晓云开发文档:https://doc.minapp.com/
node-xlsx 文档:https://www.npmjs.com/package...

五、源码

仓库地址:https://github.com/ifanrx/exp...

六、福利

即日( 3 月 8 日)起,前 50 名报名并通过审核的 Web 端公测的用户,在正式接入后即获账户礼金 100 元

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109043.html

相关文章

  • 使用 SpreadJS 实现 JavaScript 中导入和导出Excel文件

    摘要:而作为一款深受用户喜爱的电子表格工具,借助其直观的界面出色的计算性能和图表工具,已经成为数据统计领域不可或缺的软件之一。使用实现的导入和导出通过纯,您完全可以实现导入和导出文件功能,并为最终用户提供与这些文件进行交互的界面。 JavaScript是一个涵盖多种框架、直译式、可以轻松自定义客户端的脚本语言,在 Web 应用程序中,更加易于编码和维护。而Excel 作为一款深受用户喜爱的电...

    Jioby 评论0 收藏0
  • 实战PHP导出Excel-CSV文件

    摘要:中国大陆几乎所有的中文系统和国际化的软件都支持。与相应的国家标准中的其它汉字,以上合计个汉字。,全称国家标准信息技术中文编码字符集,是中华人民共和国现时最新的内码字集,是信息技术信息交换用汉字编码字符集基本集的扩充的修订版。 实战PHP导出Excel-CSV文件 导出后的效果 先给各位看一下导出的效果,而后再进行代码分析 showImg(https://segmentfault.co...

    Kyxy 评论0 收藏0
  • 实战PHP导出Excel-CSV文件

    摘要:中国大陆几乎所有的中文系统和国际化的软件都支持。与相应的国家标准中的其它汉字,以上合计个汉字。,全称国家标准信息技术中文编码字符集,是中华人民共和国现时最新的内码字集,是信息技术信息交换用汉字编码字符集基本集的扩充的修订版。 实战PHP导出Excel-CSV文件 导出后的效果 先给各位看一下导出的效果,而后再进行代码分析 showImg(https://segmentfault.co...

    molyzzx 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<