前言

当处于小图床容易跑路,云服务商oss储存又不想花钱的尴尬处境上,故想用GitHub作为免费图床托管,cloudflare进行代理加速实现图床搭建。

方案的主要思路是使用 Cloudflare 的 Workers 来代理 github 私有仓库中文件的地址,并绑定自己的域名进行使用。该方案主要优势是:

  1. Github 服务稳定,不会跑路。
  2. 使用的是 Github 的私有仓库,存储里的文件列表并不会像公开仓库一样全部对外暴露,有一定的安全性。
  3. 使用自己的域名,方便以后可能的服务迁移。
  4. 免费:GitHub和Cloudflare都提供免费的基本服务,这意味着你可以使用它们搭建图床而不需要额外的费用。
  5. 稳定性和可靠性:GitHub和Cloudflare都是知名的服务提供商,拥有稳定的基础架构和高可用性。GitHub 文件托管服务,在稳定性和可靠性方面表现良好。Cloudflare的CDN服务可以加速图像加载,提高用户体验。
  6. 全球分发:通过使用Cloudflare的CDN,你的图片可以在全球范围内快速分发,减少加载时间并提升访问速度。
  7. 自定义域名:你可以使用自己的域名来访问图床,这为你的图床增加了专业性和个性化。
  8. 版本控制:GitHub提供版本控制功能,你可以管理和跟踪你上传的图片文件的变化。这对于团队协作或者回溯图片历史非常有用。
  9. 安全性:GitHub和Cloudflare都有安全性功能,包括SSL/TLS加密和防御DDoS攻击等。这些功能可以保护你的图片和用户数据。

你需要准备的东西有:

  1. 一个域名。
  2. Cloudflare 账号。
  3. github 账号。

本篇文章参考知乎用户文章,并修改了代码的两处警告完成。感谢大佬!!!

教程

创建一个GitHub私有仓库

这个部分省略,我相信没有人不会操作。

创建GitHub令牌直达连接

点击右上角用户头像,进入 Settings(设置)页面。

在这个页面中左侧侧边栏选择 Developer settings(开发人员设置)

然后点击 Personal access tokens(个人访问令牌)菜单里的 Token classic,点击 Generate new token 开始创建一个新的令牌,注意一定要选择 classic 方式。

在创建页面中,填写 Note 为“图床”,Expiration(过期时间)为 No expiration(永久),在下面的Select scopes(选择权限范围)如下图勾选 repo。最后点击 generate token 生成令牌即可。

在生成后的页面中会看到新生成的github令牌,该令牌后面会使用到。

务必将令牌保存起来,放在一个安全的地方,页面关掉后就看不到了。

在cloudflare创建用于代理的 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 = "****";

// 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,
});

let 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;
}

需要修改的地方有

1
2
3
4
5
6
7
// Custom pathname for the upstream website.
// (1) 填写代理的路径,格式为 /<用户>/<仓库名>/<分支>
const upstream_path = "****";

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

保存并部署

绑定域名

由于cloud flare只支持托管在cloudflare的域名,所以你需要把域名的DNS指向它提供的。在cloudflare左边的网站进去之后就可以看到应该把dns服务器指向的服务器。由于我一直都用cloudflare这里就不方便做展示。

点击触发器绑定自己的域名

之后回到picgo就可配置相关的上传服务。

该封面图片由sfkjrgkPixabay上发布