本文已收录至 Butterfly 主题美化教程

使用了 CDN 加速源站的小伙伴可能会遇到一个问题,那就是每次发布博客到服务器后,都需要手动在腾讯云控制台进行节点的缓存刷新,很是麻烦。这里我介绍几种刷新 CDN 缓存的方法,对于 Hexo 博客,将着重介绍通过脚本的思路实现一键发布的具体流程。

方案一:腾讯云对象存储的 CDN 刷新

首先介绍官方方案。点击存储桶后可以找到 CDN 缓存刷新函数,这是一项收费服务。有兴趣的小伙伴可以试一试。

image.png

方案二:自写 JS 脚本并利用钩子实现(Hexo 方案)

思路(package.json 编写)

在接触 Hexo 博客建站的时候,你一定会知道 Hexo 三连:

1
2
3
hexo clean && hexo g && hexo d
# 当然可以简写成
hexo clean && hexo d -g

命令含义参考:指令 | Hexo

上面的命令可以写进 [blog_root]/package.jsonscripts 中,如:

1
2
3
4
"scripts": {
"publish": "hexo clean && hexo d -g",
"start": "hexo clean && hexo g && hexo s"
},

这样每次部署我们只需要运行 npm run publish 即可完成连击。

在完成博客部署连击后,我们可以再执行一个脚本刷新腾讯云 CDN 缓存。我打算写一个 js 脚本 auto_flush_cdn.js 用 node 运行(毕竟玩 Hexo 总会装有 Node.js 的吧)。

这样我们的部署脚本就会是这样:

1
2
3
"scripts": {
"publish": "hexo clean && hexo d -g && node .\\auto_flush_cdn.js",
},

或者利用钩子:

1
2
3
4
"scripts": {
"publish": "hexo clean && hexo d -g",
    "postpublish": "node .\\auto_flush_cdn.js",
},

现在的问题就是 auto_flush_cdn.js 该如何写。

使用腾讯云 SDK 进行开发

登录腾讯云后,打开云产品>云 API:

image-20230501153225264

我们要使用腾讯云 SDK,需要先安装依赖:

依赖下载dfsdf

这里我就安装在博客目录了:博客下新建终端运行命令 npm install tencentcloud-sdk-nodejs-cdn --save

提示

实践中遇到找不到模块的问题,安装好依赖后记得重启终端、VSCode 甚至电脑。如果不行请另外搜索解决方案。

然后找到对应的 API,按照平台指示的说明填写好参数,充分利用其诊断工具进行在线调试。(一般不会出什么问题)

线上尝试接口dfdff

没有问题的话我们就可以把平台代码复制下来了,在代码中改改 API ID 和 API KEY 参数即可。

注意

此处 API ID 和 API KEY 参数填写仅供演示。为了安全起见,建议不要在代码中写入任何的密匙。详情看下一小节。

运行一下我们的脚本文件尝试一下:

成功ddf

没有返回错误信息,看了看控制台确实进行了刷新。如果出现问题,可以把 RequestId 粘贴到 API Doctor(使用诊断) - 云 API - 控制台 (tencent.com) 进行诊断。

附控制台看刷新纪录的方法,打开内容分发网络找刷新预热的操作记录:

查看有没有刷新成功6546

安全使用 API KEY

在使用腾讯云 SDK 进行开发时,不可避免的需要传入 API KEY,但是把密匙硬写在代码中是不安全的。

注意

代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。建议不要在代码中使用持久 SecretId 和 SecretKey。

——腾讯云

这里介绍的是通过环境变量读取 SecretId 和 SecretKey 方案。

Windows

首先在自己的系统中配置环境变量,把自己的 SecretId 和 SecretKey 填进去:

示例env546546

我们可以写一个 demo 测试一下:

新建 test_env.js,写入两行代码:

1
2
var Key = process.env.TENCENT_API_SECRETID;
console.log(Key)

运行脚本:

1
2
3
node <your test_env.js>
# 例如 node .\test_env.js
# 输出 YOUR_ENV

若成功输出即配置成功。

注意

环境变量配置好后,记得重启你的终端、VSCode(如果你使用 VSCode 的终端)。太玄学的话,你懂的,重启电脑。

在你刚刚从腾讯云复制下来的代码文件中做以下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const tencentcloud = require("tencentcloud-sdk-nodejs-cdn");

const CdnClient = tencentcloud.cdn.v20180606.Client;

+ var secretId = process.env.TENCENT_API_SECRETID;
+ var secretKey = process.env.TENCENT_API_SECRETKEY;
...

const clientConfig = {
credential: {
- secretId: "SecretId",
- secretKey: "SecretKey",
+ secretId: secretId,
+ secretKey: secretKey,
},
...
};

python 中使用环境变量的方法:

1
2
3
4
5
6
7
import os
os.environ["TENCENT_API_SECRETID"]
os.environ["TENCENT_API_SECRETKEY"]

# 或
os.environ.get("TENCENT_API_SECRETID")
os.environ.get("TENCENT_API_SECRETKEY")

Linux

Linux 中需要将环境变量持久化,方法有很多,这里我就演示其中一种。

1
vim /etc/environment

添加:

1
2
export TENCENT_API_SECRETID=AKIDvhxxxxxxxxxxxwP
export TENCENT_API_SECRETKEY=Exxxxxxxxxxxxxxxxh

然后手动执行命令使其生效:

1
source /etc/environment

可以执行以下命令查看环境变量:

1
2
3
export
# declare -x TENCENT_API_SECRETID="AKIDvhxxxxxxxxxxxwP"
# declare -x TENCENT_API_SECRETKEY="Exxxxxxxxxxxxxxxxh"

这种方法:

  • 生效时间:新开终端生效,或者手动 source /etc/environment 生效
  • 生效期限:永久有效
  • 生效范围:对所有用户有效

脚本

最后的脚本文件文件:

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
// Depends on tencentcloud-sdk-nodejs version 4.0.3 or higher
const tencentcloud = require("tencentcloud-sdk-nodejs-cdn");

const CdnClient = tencentcloud.cdn.v20180606.Client;

var secretId = process.env.TENCENT_API_SECRETID;
var secretKey = process.env.TENCENT_API_SECRETKEY;

// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
const clientConfig = {
credential: {
secretId: secretId,
secretKey: secretKey,
},
region: "",
profile: {
httpProfile: {
endpoint: "cdn.tencentcloudapi.com",
},
},
};

// 实例化要请求产品的client对象,clientProfile是可选的
const client = new CdnClient(clientConfig);
const params = {
"Paths": [
/* 替换成你的网址 */
"https://uuanqin.top",
"https://www.uuanqin.top"
],
"FlushType": "delete"
};
client.PurgePathCache(params).then(
(data) => {
console.log(data);
},
(err) => {
console.error("error", err);
}
);

方案三:WordPress 解决方案

functions.php 下添加自动刷新腾讯云控制台 CDN 缓存的操作。

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
167
168
169
170
171
/* 需要填写你的密钥,访问 https://console.qcloud.com/capi 获取 SecretId 及 $secretKey */
$secretKey = '填写你的密钥';
$secretId = '填写你的密钥';

//更新或发布文章清理本文和首页CDN缓存
add_action('publish_post', 'Clean_By_Publish', 0);

//提交评论更新本文CDN缓存
add_action('comment_post', 'Clean_By_Comments',0);

//评论被审核更新本文CDN缓存
add_action('comment_unapproved_to_approved', 'Clean_By_Approved',0);

//发布文章更新CDN缓存函数
function Clean_By_Publish($post_ID){
global $secretKey,$secretId;
$url = get_permalink($post_ID);
$action='RefreshCdnUrl';
/*要清理的页面,默认包含首页和文章页面,需要清理其他页面请自行发挥*/
$PRIVATE_PARAMS = array(
'urls.0' => home_url(),
'urls.1' => $url ,
);
$HttpUrl="cdn.api.qcloud.com";
/*除非有特殊说明,如MultipartUploadVodFile,其它接口都支持GET及POST*/
$HttpMethod="POST";
/*是否https协议,大部分接口都必须为https,只有少部分接口除外(如MultipartUploadVodFile)*/
$isHttps =true;
/*下面这五个参数为所有接口的 公共参数;对于某些接口没有地域概念,则不用传递Region(如DescribeDeals)*/
$COMMON_PARAMS = array(
'Nonce' => rand(),
'Timestamp' =>time(NULL),
'Action' =>$action,
'SecretId' => $secretId,
);
/***********************************************************************************/
CreateRequest($HttpUrl,$HttpMethod,$COMMON_PARAMS,$secretKey, $PRIVATE_PARAMS, $isHttps);
}

//提交评论清理文章CDN函数
function Clean_By_Comments($comment_id)
{
global $secretKey,$secretId;
$comment = get_comment($comment_id);
$url = get_permalink($comment->comment_post_ID);
$action='RefreshCdnUrl';
/*参数*/
$PRIVATE_PARAMS = array(
'urls.0' => $url,
);
$HttpUrl="cdn.api.qcloud.com";
/*除非有特殊说明,如MultipartUploadVodFile,其它接口都支持GET及POST*/
$HttpMethod="POST";
/*是否https协议,大部分接口都必须为https,只有少部分接口除外(如MultipartUploadVodFile)*/
$isHttps =true;
/*下面这五个参数为所有接口的 公共参数;对于某些接口没有地域概念,则不用传递Region(如DescribeDeals)*/
$COMMON_PARAMS = array(
'Nonce' => rand(),
'Timestamp' =>time(NULL),
'Action' =>$action,
'SecretId' => $secretId,
);
/***********************************************************************************/
CreateRequest($HttpUrl,$HttpMethod,$COMMON_PARAMS,$secretKey, $PRIVATE_PARAMS, $isHttps);
}

//评论被审核清理CDN缓存函数
function Clean_By_Approved($comment)
{
global $secretKey,$secretId;
$url = get_permalink($comment->comment_post_ID);
$action='RefreshCdnUrl';
/*参数*/
$PRIVATE_PARAMS = array(
'urls.0' => $url,
);
$HttpUrl="cdn.api.qcloud.com";
/*除非有特殊说明,如MultipartUploadVodFile,其它接口都支持GET及POST*/
$HttpMethod="POST";
/*是否https协议,大部分接口都必须为https,只有少部分接口除外(如MultipartUploadVodFile)*/
$isHttps =true;
/*下面这五个参数为所有接口的 公共参数;对于某些接口没有地域概念,则不用传递Region(如DescribeDeals)*/
$COMMON_PARAMS = array(
'Nonce' => rand(),
'Timestamp' =>time(NULL),
'Action' =>$action,
'SecretId' => $secretId,
);
/***********************************************************************************/
CreateRequest($HttpUrl,$HttpMethod,$COMMON_PARAMS,$secretKey, $PRIVATE_PARAMS, $isHttps);
}

//请求主函数,无需改动
function CreateRequest($HttpUrl,$HttpMethod,$COMMON_PARAMS,$secretKey, $PRIVATE_PARAMS, $isHttps)
{
$FullHttpUrl = $HttpUrl."/v2/index.php";
/***************对请求参数 按参数名 做字典序升序排列,注意此排序区分大小写*************/
$ReqParaArray = array_merge($COMMON_PARAMS, $PRIVATE_PARAMS);
ksort($ReqParaArray);
/**********************************生成签名原文**********************************
* 将 请求方法, URI地址,及排序好的请求参数 按照下面格式 拼接在一起, 生成签名原文,此请求中的原文为
* GETcvm.api.qcloud.com/v2/index.php?Action=DescribeInstances&Nonce=345122&Region=gz
* &SecretId=AKIDz8krbsJ5yKBZQ ·1pn74WFkmLPx3gnPhESA&Timestamp=1408704141
* &instanceIds.0=qcvm12345&instanceIds.1=qcvm56789
* ****************************************************************************/
$SigTxt = $HttpMethod.$FullHttpUrl."?";
$isFirst = true;
foreach ($ReqParaArray as $key => $value)
{
if (!$isFirst)
{
$SigTxt = $SigTxt."&";
}
$isFirst= false;
/*拼接签名原文时,如果参数名称中携带_,需要替换成.*/
if(strpos($key, '_'))
{
$key = str_replace('_', '.', $key);
}
$SigTxt=$SigTxt.$key."=".$value;
}
/*********************根据签名原文字符串 $SigTxt,生成签名 Signature******************/
$Signature = base64_encode(hash_hmac('sha1', $SigTxt, $secretKey, true));
/***************拼接请求串,对于请求参数及签名,需要进行urlencode编码********************/
$Req = "Signature=".urlencode($Signature);
foreach ($ReqParaArray as $key => $value)
{
$Req=$Req."&".$key."=".urlencode($value);
}
/*********************************发送请求********************************/
if($HttpMethod === 'GET')
{
if($isHttps === true)
{
$Req="https://".$FullHttpUrl."?".$Req;
}
else
{
$Req="http://".$FullHttpUrl."?".$Req;
}
$Rsp = file_get_contents($Req);
}
else
{
if($isHttps === true)
{
$Rsp= SendPost("https://".$FullHttpUrl,$Req,$isHttps);
}
else
{
$Rsp= SendPost("http://".$FullHttpUrl,$Req,$isHttps);
}
}
//var_export(json_decode($Rsp,true));
return json_decode($Rsp,true);
}
function SendPost($FullHttpUrl, $Req, $isHttps)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $Req);
curl_setopt($ch, CURLOPT_URL, $FullHttpUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 1 );
if ($isHttps === true) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
$result = curl_exec($ch);
return $result;
}

本文参考