定期自动将云端数据库备份至云存储中

微信小程序使用的是云开发,之前都是通过手工保存云端数据库中多个表单的方式,以此进行数据备份。显然不够智能,也比较浪费时间。所以,此次写了个云函数,命名为autoBackupDatabase,设置好触发器后,即可自动将云数据库保存至云存储中。

云数据库备份至云存储的主要步骤:

  1. 取得access_token;
  2. 取得数据库表单查询后的job_id;
  3. 通过取得的job_id获取到对应的数据库备份文件的url;
  4. 通过数据库备份文件的url,取得该url对应的文件内容;
  5. 将取得的文件内容上传至云存储中;
  6. 上传云函数并设置触发器

以下为autoBackupDatabase云函数的全部代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// 云函数入口文件
const cloud = require('wx-server-sdk');
const request = require('request');
cloud.init({ env: 'xxxxxx' }); // 使用当前云环境

// 1.
// 获得access_token
async function getAccessToken(appid, secret) {
return new Promise((resolve, reject) => {
request.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`,
(err, res, body) => {
if (err) {
reject(err)
}
resolve(JSON.parse(body))
}
)
})
}

// 2.
// 进行数据库的查询操作,并导出一个job_id以获取文件地址
async function createExportJob(access_token, env, collection) {
const date = new Date().toISOString();

return new Promise((resolve, reject) => {
request.post(
`https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${access_token}`,
{
body: JSON.stringify({
env, // 云开发的环境ID
file_path: `${date}.json`, // 准备生成的文件名称
file_type: '1', // '1' 为 JSON, '2' 为 CSV
query: `db.collection("${collection}").get()` // 预设的查询命令
})
},
(err, res, body) => {
if (err) {
reject(err)
}

resolve(JSON.parse(body))
}
)
})
}

// 3.
// 从 access_token 和 job_id 获取到 目标文件的url,
// 防止无法取到值,设置了一个500ms的setInterval去不断执行。
async function waitJobFinished(access_token, env, job_id) {
return new Promise((resolve, reject) => {
const timer = setInterval(() => {
request.post(
`https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${access_token}`,
{
body: JSON.stringify({
env,
job_id
})
},
(err, res, body) => {
if (err) {
reject(err)
}

const { status, file_url } = JSON.parse(body)

console.log("查询")

if (status === 'success') {
clearInterval(timer)
resolve(file_url)
}
}
)
}, 500)
})
}

// 4.
// 从目标文件的url取得具体内容
async function getJsonContent(file_url) {
return new Promise((resolve, reject) => {
request.get(
`${file_url}`,
(err, res, body) => {
if (err) {
reject(err)
}
resolve(body)
}
)
})
}

// 云函数入口函数
exports.main = async (event, context) => {
const appid = "xxxxxxx";
const secret = "xxxxxxx";
const envId = "xxxxxxx" // 云开发的环境ID
// 需要备份的集合名称
const backupFromTables = ["table1", "table2", "table3"];
// 云端备份所存放的目录
const folderName = "databaseBackup"
const dateString = (new Date()).toISOString()

// count是为统计backupFromTables中已完成备份多少张表单的计数器
var count = 0
var access_token = ""

// 1.
try {
const result = await getAccessToken(appid, secret)
access_token = result.access_token

if (typeof access_token == null || access_token== "" || access_token == "undefined") {
throw new Error(`获取 access_token 失败: ${result.errmsg}` || '获取 access_token 为空')
}
} catch (e) {
throw new Error(`获取 access_token: ${e.message}`);
}

for(index in backupFromTables) {
const backupFromTable = backupFromTables[index]

// 2.
createExportJob(access_token, envId, backupFromTable)
.then(res => {
console.log(res)
if(res.errcode !== 0) {
throw new Error("Can't get job_id")
}
console.log(res.job_id)
// 3.
return waitJobFinished(access_token, envId, res.job_id)
})
.then(file_url => {
// 4.
return getJsonContent(file_url)
})
.then(fileContent => {
// 5.
return cloud.uploadFile({
cloudPath: './' + folderName + '/' + dateString.slice(0,10) + '/' + dateString + '_' + backupFromTable + '.json',
fileContent: fileContent
})
})
.then(res => {
count++
console.log("--->---- " + count + "/" + backupFromTables.length+ ":" + backupFromTable + "已完成备份 ---<----")
})
.catch( error => {
throw new Error(`导出数据库异常: ${error.message}`);
})
}

}