在日常的工作中,常常需要根據運營需求對資料進行各種格式的處理和匯出。匯出後,不少人偏愛將資料放入 excel 在進行處理。
一般來說,處理資料匯出時需要對資料進行一些運算整理。在以前,處理的方式是在一臺獨立的伺服器上跑指令碼。
而現在有了知曉雲,不再需要維護伺服器,直接寫程式碼就能把相關事都都丟給雲函式。 本文將介紹通過知曉云云函式來實現將資料表匯出為 excel 檔案的功能,並使用 webpack
和 mincloud
將程式碼打包上傳到知曉雲。
技術棧:
- 打包工具:
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/node-xlsx
五、原始碼
倉庫地址:https://github.com/ifanrx/export-excel-file
六、福利
即日( 3 月 8 日)起,前 50 名報名並通過稽核的 Web 端公測的使用者,在正式接入後即獲賬戶禮金 100 元。?
報名點這裡 ? 知曉雲公測活動 ? 除了這個好訊息,小云還要告訴大家,公測活動結束後還將挑選出 5 名積極反饋的使用者獲得知曉雲限量紀念 T 恤呀
(。・ω・。)ノ♡
本文首發於「知曉雲」公眾號:https://mp.weixin.qq.com/s/g67uTqnWTIsGiqtKdl-W4w
知曉雲是國內首家專注於小程式開發的後端雲服務。使用知曉雲,小程式開發快人一步。