优化github_actions上传腾讯云cos逻辑

使用https://github.com/landv/hexo-deployer-cos库进行上传cos

之前使用上传方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- name: 8. 安装COS相关依赖
run: |
pip install coscmd
pip install tccli
- name: 9. 配置COS
env:
SECRET_ID: ${{ secrets.SECRET_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
BUCKET: ${{ secrets.BUCKET }}
REGION: ${{ secrets.REGION }}
run: |
coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
tccli configure set secretId $SECRET_ID
tccli configure set secretKey $SECRET_KEY
tccli configure set region $REGION
- name: 10. 上传文档到cos并刷新CDN
run: |
ls -al ./public
rm -rf ./public/.git/
coscmd upload -rfs --delete ./public/ /
tccli cdn PurgePathCache --cli-unfold-argument --Paths https://landv.cn/ --FlushType flush

因为网络问题,上传会出现失败或者上传时间长。尤其是文章多了以后。因为这种方式是全量上传。

现在使用自定义库进行上传

1
2
3
4
5
6
7
8
9
10
- name: 6. 生成静态文件并上传cos
env:
COS_SECRET_ID: ${{ secrets.SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.SECRET_KEY }}
COS_BUCKET: ${{ secrets.BUCKET }}
COS_REGION: ${{ secrets.REGION }}
run: |
hexo clean
hexo generate
hexo deploy

使用的hexo deploy 方式,其中hexo-deployer-cos库进行了优化,不再是全量上传,以差异上传的方式加速。

具体逻辑https://github.com/landv/hexo-deployer-cos/commit/6cdf0f56139f1e13472a2d7c5bca785226e48ee2

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
160
161
162
163
164
165
166
'use strict'

const os = require('os')
const fs = require('hexo-fs')
const chalk = require('chalk')
const COS = require('cos-nodejs-sdk-v5')
const crypto = require('crypto')

module.exports = function (args) {
// Hexo's Logger
let log = this.log

if (!args.secretId) {
args.secretId = process.env.COS_SECRET_ID
}
if (!args.secretKey) {
args.secretKey = process.env.COS_SECRET_KEY
}
if (!args.bucket) {
args.bucket = process.env.COS_BUCKET
}
if (!args.region) {
args.region = process.env.COS_REGION
}

// Check the user's configuration
if (!checkHexoConfig(args)) {
log.error('hexo-deployer-cos: config error')
return
}

// Get local files list from Public Directory
let localFiles = fs.listDirSync(this.public_dir)

// Create COS object
let cos = new COS({
SecretId: args.secretId,
SecretKey: args.secretKey
})

// Bucket's configuration
const bucketConfig = {
Bucket: args.bucket,
Region: args.region
}

log.info('Uploading files to COS...')

// Get all files from the Bucket

cos.getBucket({
Bucket: bucketConfig.Bucket,
Region: bucketConfig.Region
})

listObjects(cos, bucketConfig)
.then(data => {
localFiles.map(file => {
getFileMD5(this.public_dir + file)
.then((etag) => {
if (!data.includes(etag)) {
log.info('Upload: ' + file)
uploadFileToCOS(cos, bucketConfig, {
path: this.public_dir + file,
name: getFileName(file)
})
}
})
})
}).catch(err => {
log.error(err.error.Code + ': ' + err.error.Message)
})
}

/**
* Check if the configuration is correct in _config.yml file
* @param {string} args
* @return {boolean}
*/
function checkHexoConfig (args) {
if (!args.secretId ||
!args.secretKey ||
!args.bucket ||
!args.region) {
let tips = [
chalk.red('Ohh~We have a little trouble!'),
'Please check if you have made the following settings',
'deploy:',
' type: cos',
' secretId: yourSecretId',
' secretKey: yourSecretKey',
' bucket: yourBucket',
' region: yourRegion',
'',
'Need more help? You can check the Tencent cloud document: ' + chalk.underline('https://www.qcloud.com/document/product/436')
]
console.log(tips.join('\n'))
return false
} else {
return true
}
}

/**
* Upload file to COS
* @param {object} cos
* @param {object} config
* @param {object} file
*/
function uploadFileToCOS (cos, config, file) {
return new Promise((resolve, reject) => {
cos.putObject({
Bucket: config.Bucket,
Region: config.Region,
Key: file.name,
Body: fs.createReadStream(file.path)
}, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}

function listObjects (cos, config) {
return new Promise((resolve, reject) => {
cos.getBucket({
Bucket: config.Bucket,
Region: config.Region
}, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data.Contents.map(obj => obj.ETag.slice(1, -1)))
}
})
})
}

/**
* Get file md5
* @param {string} path
*/
function getFileMD5 (path) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('md5')
const stream = fs.createReadStream(path)
stream.on('error', err => reject(err))
stream.on('data', chunk => hash.update(chunk))
stream.on('end', () => resolve(hash.digest('hex')))
})
}

/**
* if OS is Windows, replace the path specific to '/' as filename
* @param {string} filename
* @returns {string} filename
*/
function getFileName (filename) {
if (os.platform() === 'win32') {
return filename.replace(/\\/g, '/')
}
return filename
}