对于个人博客,图床一直是一个比较麻烦的问题。个人难以承受对象储存高额的流量费;如果使用云服务器自建图床,部署和迁移都比较麻烦;如果使用一些公开的图床,很难保证安全性和持久性。本人最近在知乎上看到一篇文章,使用Github+Clouflare搭建个人图床,觉得是一个比较理想的方案,故采用了这个方案并写下这篇笔记,以便之后参考。

1. 在 Github 上新建一个私有仓库

此部分比较简单,注意选择Private即可。当然,选择Public也没有问题。

2. 在 Github 上生成访问令牌

在 GitHub 上生成一个 Personal access token,用于身份验证。
直达链接:https://github.com/settings/tokens

Note自己填写,可以写picture、图床之类的;Expiration选择No Expiration,或者每隔一段时间自己更换token,repo处打钩即可。

注意:token务必保存起来,页面关掉后就看不到了。

3. 在 Cloudflare 上创建 Worker

创建 Worker

登录到Cloudflare,点击侧边栏的“Workers”选项,然后点击“创建服务”创建一个 Worker。

名称自己定义,然后点击部署。

部署代码

部署完成后编辑代码,代码填写下面这段代码:

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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Website you intended to retrieve for users.
const upstream = "raw.githubusercontent.com";

// Custom pathname for the upstream website.
// (1) 填写代理的路径,格式为 /<用户>/<仓库名>/<分支>
const upstream_path = "/user/repo/main";

// github personal access token.
// (2) 填写github令牌
const github_token = "";

// Website you intended to retrieve for users using mobile devices.
const upstream_mobile = upstream;

// Countries and regions where you wish to suspend your service.
const blocked_region = [];

// IP addresses which you wish to block from using your service.
const blocked_ip_address = ["0.0.0.0", "127.0.0.1"];

// Whether to use HTTPS protocol for upstream address.
const https = true;

// Whether to disable cache.
const disable_cache = false;

// Replace texts.
const replace_dict = {
$upstream: "$custom_domain",
};

addEventListener("fetch", (event) => {
event.respondWith(fetchAndApply(event.request));
});

async function fetchAndApply(request) {
const region = request.headers.get("cf-ipcountry")?.toUpperCase();
const ip_address = request.headers.get("cf-connecting-ip");
const user_agent = request.headers.get("user-agent");

let response = null;
let url = new URL(request.url);
let url_hostname = url.hostname;

if (https == true) {
url.protocol = "https:";
} else {
url.protocol = "http:";
}

if (await device_status(user_agent)) {
var upstream_domain = upstream;
} else {
var upstream_domain = upstream_mobile;
}

url.host = upstream_domain;
if (url.pathname == "/") {
url.pathname = upstream_path;
} else {
url.pathname = upstream_path + url.pathname;
}

if (blocked_region.includes(region)) {
response = new Response(
"Access denied: WorkersProxy is not available in your region yet.",
{
status: 403,
}
);
} else if (blocked_ip_address.includes(ip_address)) {
response = new Response(
"Access denied: Your IP address is blocked by WorkersProxy.",
{
status: 403,
}
);
} else {
let method = request.method;
let request_headers = request.headers;
let new_request_headers = new Headers(request_headers);

new_request_headers.set("Host", upstream_domain);
new_request_headers.set("Referer", url.protocol + "//" + url_hostname);
new_request_headers.set("Authorization", "token " + github_token);

let original_response = await fetch(url.href, {
method: method,
headers: new_request_headers,
body: request.body,
});

connection_upgrade = new_request_headers.get("Upgrade");
if (connection_upgrade && connection_upgrade.toLowerCase() == "websocket") {
return original_response;
}

let original_response_clone = original_response.clone();
let original_text = null;
let response_headers = original_response.headers;
let new_response_headers = new Headers(response_headers);
let status = original_response.status;

if (disable_cache) {
new_response_headers.set("Cache-Control", "no-store");
} else {
new_response_headers.set("Cache-Control", "max-age=43200000");
}

new_response_headers.set("access-control-allow-origin", "*");
new_response_headers.set("access-control-allow-credentials", true);
new_response_headers.delete("content-security-policy");
new_response_headers.delete("content-security-policy-report-only");
new_response_headers.delete("clear-site-data");

if (new_response_headers.get("x-pjax-url")) {
new_response_headers.set(
"x-pjax-url",
response_headers
.get("x-pjax-url")
.replace("//" + upstream_domain, "//" + url_hostname)
);
}

const content_type = new_response_headers.get("content-type");
if (
content_type != null &&
content_type.includes("text/html") &&
content_type.includes("UTF-8")
) {
original_text = await replace_response_text(
original_response_clone,
upstream_domain,
url_hostname
);
} else {
original_text = original_response_clone.body;
}

response = new Response(original_text, {
status,
headers: new_response_headers,
});
}
return response;
}

async function replace_response_text(response, upstream_domain, host_name) {
let text = await response.text();

var i, j;
for (i in replace_dict) {
j = replace_dict[i];
if (i == "$upstream") {
i = upstream_domain;
} else if (i == "$custom_domain") {
i = host_name;
}

if (j == "$upstream") {
j = upstream_domain;
} else if (j == "$custom_domain") {
j = host_name;
}

let re = new RegExp(i, "g");
text = text.replace(re, j);
}
return text;
}

async function device_status(user_agent_info) {
var agents = [
"Android",
"iPhone",
"SymbianOS",
"Windows Phone",
"iPad",
"iPod",
];
var flag = true;
for (var v = 0; v < agents.length; v++) {
if (user_agent_info.indexOf(agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}

注意修改代码中的 upstream_pathgithub_token

然后点击部署:

配置自定义域名

笔者域名托管在Cloudflare,如果域名托管在其他地方,可能下面的方法不适用。
进入对应的Worker,点击设置、触发器、添加自定义域,输入自己的域名即可。

4. 配置 PicGo

下载 PicGo

PicGo文档:https://picgo.github.io/PicGo-Doc/zh/

使用 Github 作为图床

在侧边栏找到“图床设置”中的“Github”,点击“+”号。

配置名自定义,其他根据实际情况填写即可。储存路径是指储存在Github仓库中的哪个文件夹下,自定义域名只是方便用户直接获取图片的链接地址。

如有需要,可以设置一下这个。

最后别忘记设置默认图床