作者:知曉雲 - 小程序開發快人一步
來源:知曉課堂
在日常的工作中,常常需要根據運營需求對數據進行各種格式的處理和導出。導出後,不少人偏愛將數據放入 excel在進行處理。
一般來說,處理數據導出時需要對數據進行一些運算整理。在以前,處理的方式是在一臺獨立的服務器上跑腳本。
而現在有了知曉雲,不再需要維護服務器,直接寫代碼就能把相關事都都丟給雲函數。 本文將介紹通過知曉云云函數來實現將數據表導出爲 excel 文件的功能,並使用 webpack 和 mincloud 將代碼打包上傳到知曉雲。
技術棧:
- 打包工具:[email protected]
- 部署工具:[email protected]
- Excel 處理:[email protected]
- 其他:知曉雲 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 一樣,部署前需要先登錄,請參照文檔配置。
使用以下命令即可將雲函數部署到知曉雲:
run deploy
執行結果如下:
使用以下的命令來測試:
mincloud invoke export-excel-filebr
執行結果如下:
export_task 表記錄:
上傳到知曉雲的 excel 文件如下:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8xgPW2iK-1588065248145)(https://cloud-minapp-11491.cloud.ifanrusercontent.com/1h3eeGNECR14zCdk.png)]
文件內容:
四、參考文檔
知曉雲開發文檔:https://doc.minapp.com/
node-xlsx 文檔:https://www.npmjs.com/package/node-xlsx
五、源碼
倉庫地址:https://github.com/ifanrx/export-excel-file
相關閱讀
第一期:快速實現圖片爬蟲
第二期:快速生成分享海報
第三期:處理微信卡券消息
第四期:自動回覆客服消息
第五期:生成帶參數的二維碼
關注「知曉雲」公衆號,點擊菜單欄「知曉雲」-「知曉課堂」,獲取更多開發教程。