Hexo使用PythonSDK整站静态发布七牛云

前言

最近因GitLab的国内访问速度问题将本站静态化到了七牛云存储,但是带来的问题也是很明显的。之前在GitLab使用Page来发布工程只需要提交代码即可,会自动静态化并部署项目,于是乎我就想让静态化并同步到七牛对象存储自动化一些,这也是这篇文章的初衷。

其实在用Python做自动同步之前,我使用的官方提供的一个Window上面的同步工具QSunSync,但是使用起来也不是很方便,最主要的原因是不太符合我整站同步的需求,而且速度极慢。

Python SDK

官网为我们提供了基础的对象存储操作SDK, 点击查看

安装Python SDK

1
pip install qiniu

整个同步过程如下:

  1. 拉取七牛云文件列表。
  2. 获取本地文件列表。
  3. 对比远程文件是否有增量(多余的),进行删除。
  4. 对比本地文件是否有增量(新增的)或者有变化(hash不同),进行上传。

同步脚本文件

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
from qiniu import Auth, put_file, etag, urlsafe_base64_encode, BucketManager, CdnManager
from typing import List, Dict
import os

from qiniu import build_batch_delete


class Sync:
"""
同步目录至七牛云
"""

def __init__(
self,
access_key: str,
secret_key: str,
bucket_name: str,
sync_dir: str,
exclude: List,
cover: bool,
remove_redundant: bool,
host: str,
):
self.bucket_name = bucket_name
self.q = Auth(access_key, secret_key)
self.bucket = BucketManager(self.q)
self.sync_dir = sync_dir
self.exclude = exclude
self.cover = cover
self.remove_redundant = remove_redundant
self.host = host
self.sync()

def sync(self):
"""
同步操作
:return:
"""
remote_files = self.list_remote()

local_files = self.list_local()

# 首先删除远端仓库中多余的文件
remove_remote_files = []
for remote_filename in remote_files:
if remote_filename not in local_files:
remove_remote_files.append(remote_filename)
print('delete remote file size = ' + str(len(remove_remote_files)))
self.bucket.batch(build_batch_delete(self.bucket_name, remove_remote_files))

cdnManager = CdnManager(self.q)
refreshUrls = []
refreshDirs = [self.host]
# 上传本地文件到远端(仅上传远端不存在的以及修改过的)
for local_filename in local_files:
if (
local_filename not in remote_files
or local_files[local_filename]["hash"]
!= remote_files[local_filename]["hash"]
):
ret, info = put_file(
self.q.upload_token(self.bucket_name, local_filename, 3600),
local_filename,
local_files[local_filename]["fullpath"],
)
remotepath = self.host + ret["key"]
print('uploaded: ' + remotepath)
#refreshUrls.append(remotepath)

print('uploaded size = ' + len(refreshUrls))
# 刷新节点资源
cdnManager.refresh_dirs(refreshDirs)
print('sycn completed !!!!')

def list_remote(self) -> Dict:
"""
列出远程仓库所有的文件信息
:return: List
"""
result = {}
marker = None
ret = self.bucket.list(self.bucket_name, marker=marker, limit=500)[0]
for file in ret["items"]:
result[file["key"]] = file
if("marker" in ret):
marker = ret["marker"]
while(marker):
print('marker = ' + marker)
ret = self.bucket.list(self.bucket_name, marker=marker, limit=500)[0]
for file in ret["items"]:
result[file["key"]] = file
if("marker" in ret):
marker = ret["marker"]
else:
break
print('pull remote file info success')
return result

def list_local(self) -> Dict:
"""
列出本地仓库所有的文件信息
"""
files = {}

def get_files(path):
for filename in os.listdir(path):
if filename in self.exclude:
continue
fullpath = os.path.join(path, filename)
if os.path.isfile(fullpath):
key = fullpath.split(self.sync_dir)[1]
foldpath = key.replace("\\","/") #实际是相对路径
files[foldpath] = {"fullpath": fullpath, "hash": etag(fullpath)}
else:
get_files(fullpath)
get_files(self.sync_dir)
print('list local file info success')
return files


if __name__ == "__main__":
Sync(
access_key="nzt90pr6nkba0dG1TD2oMul0XHBe6ALuBo3hHc_l", # access_key
secret_key="yoRI0b8J9dDiYwtJeE844tYbrupdjSiVio6sczf1", # secret_key
bucket_name="blog", # bucket_name
#sync_dir="~/blog/public/", # 静态文件目录(后面必须有斜杠/)
sync_dir="D:\\blog\\gitlab\\public\\",
exclude=[".DS_Store"],
cover=True,
remove_redundant=True,
host="https://dp2px.com/",
)

如何使用

如果你也想和我一样将整站的资源同步到七牛云,则只需要第一步使用hexo g来生成静态资源到public目录,然后配置上面Sync类的构造参数,最后执行python blog-qiniu.py即可。

1
2
3
4
5
6
7
Sync(
access_key="xxxxxx", # access_key
secret_key="xxxxxx", # secret_key
bucket_name="blog", # bucket_name
sync_dir="D:\\blog\\gitlab\\public\\", #本地public静态资源目录
host="https://dp2px.com/", #网站host,用于刷新cdn缓存
)

上面值得注意的是代码中的七牛CDN缓存刷新,官方限制了每日100条缓存文件刷新和10条缓存目录刷新,显然这100条是不够用的,所以每次提交完后我会使用refresh_dirs(refreshDirs)主动刷新更目录一次。或者可以换个思路、刷新主页面几个入口界面即可。

1
2
3
4
5
6
7
8
9
10
11
refreshUrls = [
self.host + 'index.html' # 刷新首页

]
refreshDirs = [
self.host + 'tags/', # 刷新标签页
self.host + 'categories/', # 刷新分类页
self.host + 'archives/' # 刷新归档页
]
cdnManager = CdnManager(self.q)
cdnManager.refresh_urls_and_dirs(refreshUrls, refreshDirs)

除了上面使用官方提供的SDK去刷新外,还可以去融合CDN的刷新预取界面手动操作刷新。

有的朋友可能会发现刷新后去浏览器访问并没有发生改变,那是因为在七牛对象存储的设置界面有一个maxAge缓存时间设置,我建议设置成1000,也就是17分钟左右后会重新请求新页面。

七牛对象存储maxAge设置

最后提一下,本站对整站资源进行了压缩处理,以便有更快的访问速度,安装npm install hexo-neat --save插件,在根目录_config.yml文件中配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 文件压缩,设置一些需要跳过的文件 
# hexo-neat
neat_enable: true
# 压缩 html
neat_html:
enable: true
exclude:
# 压缩 css
neat_css:
enable: true
exclude:
- '**/*.min.css'
# 压缩 js
neat_js:
enable: true
mangle: true
output:
compress:
exclude:
- '**/*.min.js'
- '**/jquery.fancybox.pack.js'
- '**/index.js'

在执行hexo g的同时会压缩静态网站资源,如果发生错误不用理会,不会影响实际展示,只是部分页面得不到正确压缩而已。

GitLab CI/CD

GitLab提供持续集成服务。如果添加一个.gitlab-ci.yml文件到项目根目录,并配置GitLab项目使用某个Runner,然后每一次提交或者是推送都会触发CI pipeline.

.gitlab-ci.yml文件会告诉GitLab Runner 做什么。默认情况下,它运行一个pipeline,分为三个阶段:build,test,deploy。你并不需要用到所有的阶段,没有job的阶段会被忽略。

简而言之,CI所需要的步骤可以归结为:

  1. 添加.gitlab-ci.yml到项目的根目录
  2. 配置一个Runner

从此刻开始,在每一次push到Git仓库的过程中,Runner会自动开启pipline,pipline将显示在项目的Pipline页面中。

GitLab CI/CD

接下来我们可以配置.gitlab-ci.yml来自动静态编译压缩和执行python脚本。

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
before_script:
- apt-get update -qq && apt-get install -y -qq pandoc
image: node:10.15.1
pages:
cache:
paths:
- node_modules/
script:
- git config --global user.email "lxq_xsyu@163.com"
- git config --global user.name "lxqxsyu"
- npm install hexo-cli -g
- npm install
- npm uninstall hexo-renderer-marked@0.3.2 --save
- npm install hexo-generator-sitemap@1.2.0 --save
- npm install hexo-generator-baidu-sitemap@0.1.5 --save
- npm install hexo-util@0.6.3 --save
- npm install hexo-generator-search@2.3.0 --save
- npm install image-size@0.6.3 --save
- npm install hexo-asset-image@0.0.3 --save
- npm install hexo-generator-feed@1.2.2 --save
- npm install hexo-neat --save
- npm install hexo-helper-live2d@3.1.1 --save
- hexo g
- python --version
- apt-get -y install python-pip
- pip install qiniu
- pip install typing
- python blog-qiniu.py
artifacts:
paths:
- public
only:
- master

本来这些配置以及可以解决问题,只需要提交代码就可以自动执行,但是在执行过程中发现镜像中的python版本是2.7的,所以我们需要修改之前编写的blog-qiniu.py中的部分代码来适应低版本, 例如asii编码问题需要添加如下代码:

1
2
3
4
5
6
# -*- coding: utf-8 -*-
#!/usr/bin/python
import sys

reload(sys)
sys.setdefaultencoding('utf8')