使用github私有仓库和Cloudflare Workers搭建个人图床+PicGo 图床上传软件 2023-06-27 学习笔记 暂无评论 118 次阅读 *注:本文是学习备忘转载,原文链接[使用github私有仓库和Cloudflare Workers搭建个人图床](https://zhuanlan.zhihu.com/p/626135137?utm_id=0)* 之前一直在使用 GitHub 的公开仓库和 jsdelivr 代理作为个人图床,直到最近 jsdelivr 喜提认证无法使用。尽管还有其他的 CDN 代理可以使用,但是还是有被墙的风险,到时候修改图片链接就太麻烦了。如需稳定的使用图床,需要自己使用服务器对 GitHub 进行反代或购买云存储服务,增加了成本。近期研究后,发现了一个几乎零成本的解决方案,用作本人目前图床方案,推荐给大家。 该方案的主要思路是使用 Cloudflare 的 Workers 来代理 github 私有仓库中文件的地址,并绑定自己的域名进行使用。该方案主要优势是: 1. Github 服务稳定,不会跑路。 1. 相比有免费额度的存储服务-七牛云和backblaze等,github没有额度的限制。 1. 使用的是 Github 的私有仓库,存储里的文件列表并不会像公开仓库一样全部对外暴露,有一定的安全性。 1. 使用自己的域名,方便以后可能的服务迁移。 1. Cloudflare Workers每天有10万次的免费请求额度,正常使用不可能用完。 使用该方案你需要以下东西: 1. 一个没有被墙的域名。在阿里云上购买`.top`域名,10年也才100多块。 1. Cloudflare 账号。免费,直接注册即可。 1. github 账号,也是免费。 首先,在github上建一个私有仓库 ![](https://pic2.zhimg.com/v2-ca66892063bce6c112e8f6fddaf4266d_b.jpg) 我这里已经建过仓库名了 然后,在 GitHub 上生成一个 Personal access token(个人访问令牌),用于身份验证。在 GitHub 网站上登录账户,点击右上角用户头像,进入 Settings(设置)页面。在这个页面中左侧侧边栏选择 Developer settings(开发人员设置),然后点击 Personal access tokens(个人访问令牌)菜单里的 Token classic,点击 Generate new token 开始创建一个新的令牌,注意一定要选择 classic 方式。 [创建github令牌直达链接](https://link.zhihu.com/?target=https%3A//github.com/settings/tokens) ![](https://pic3.zhimg.com/v2-d445243dbb4434f832abdc8b9268cbe6_b.jpg) 在创建页面中,填写 Note 为“图床”,Expiration(过期时间)为 No expiration(永久),在下面的Select scopes(选择权限范围)如下图勾选 repo。最后点击 generate token 生成令牌即可。 ![](https://pic3.zhimg.com/v2-1ee49968f8447e3346c213daa9759c1e_b.jpg) 在生成后的页面中会看到新生成的github令牌,该令牌后面会使用到。 > **务必将令牌保存起来**,放在一个安全的地方,页面关掉后就看不到了。 ## 在 Cloudflare 上创建用于代理的 Worker 登录到 Cloudflare 的管理界面后,点击侧边栏的 “Workers” 选项,然后点击 “创建服务” 创建一个 Worker。 ![](https://pic4.zhimg.com/v2-be9b77ae3f9b88bd723a60bf8ed8bfff_b.jpg) 在创建服务界面中,填写“服务名称”,选择“HTTP 处理程序”,点击“创建服务”。 ![](https://pic1.zhimg.com/v2-3d52c6d439a3b736e355708c6a9c706c_b.jpg) 在创建服务详情页中点击“快速编辑”。 ![](https://pic3.zhimg.com/v2-b62bd8e073ba7985a6bfaff1be55b9ce_b.jpg) 将下面的代码复制粘贴到编辑页面的代码编辑器中。 ``` // Website you intended to retrieve for users. const upstream = "raw.githubusercontent.com"; // Custom pathname for the upstream website. // (1) 填写代理的路径,格式为 /<用户>/<仓库名>/<分支> const upstream_path = "/wdsjxh/Pictures/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; } ``` ![](https://pic4.zhimg.com/v2-63c67eac9f0d8b34c65fe223ed48fb6f_b.jpg) 如图,有2个地方的代码需要自己修改一下。第一个是代理的路径,需要改写成自己的用户/仓库/分支,用户和仓库可以在github私有仓库页的url上看到,如`https://github.com/wdsjxh/Pictures/`,wdsjxh是用户名,Pictures是仓库名,分支现在一般默认是main,之前是叫master。第二是令牌,需要修改成之前github上申请的令牌。 大概讲一下,这串代码的作用: 1. 反向代理了github仓库。 1. 使用令牌获取文件。 1. 开启了缓存,避免重复请求图片。 最后“保存并部署”,服务就部署成功了。Cloudflare 会自动给新创建的 Worker 服务分配域名,但是这个域名非常容易被墙,接下来需要给 Worker 绑定自己购买的域名。 ## 将域名 NS 转到 Cloudflare Cloudflare Workers 的域名绑定仅支持托管在 Cloudflare 上的域名,所以得先将域名的 NS 转到 Cloudflare。大概步骤如下: 1. 单击菜单栏上的“添加站点”按钮,输入您的网站域名并单击“添加站点”。 1. Cloudflare 会在 DNS 扫描期间列出您的网站 DNS 记录。单击下拉菜单中的“继续设置”。 1. Cloudflare 会提示您选择服务计划。您可以选择“免费”或“收费”计划。您可以单击“继续”并在下一页上单击“继续设置”。 1. 接下来,Cloudflare 会生成替换您原来的 DNS 记录的新 DNS 记录。请将这些新 DNS 记录复制并粘贴到您的域名注册商的 DNS 设置中。 1. 在您的域名注册商的网站上,导航到域名管理区域,并找到 NS 记录设置。删除原来的 NS 记录,将它们替换为 Cloudflare 提供的新 NS 记录。 1. 保存更改并等待 DNS 记录更新。这通常需要几小时。 ## 给创建的 Worker 服务绑定自己的域名 域名 NS 转到 Cloudflare成功后,在 Worker 服务的详情页点击“触发器”,然后点击“添加自定义域”。 ![](https://pic3.zhimg.com/v2-2a5943e4f34cc0087c7ef90a73328892_b.jpg) 输入想要绑定的域名后,点击“添加自定义域”。 ![](https://pic3.zhimg.com/v2-10e02f3d8e24fb1fdb9cdc66fc923e3a_b.jpg) 注意,**证书**这里需要个小几分钟才会生成为有效状态,到这里,Cloudflare的配置就完成了。下面需要配置图床软件和测试一下。 ## 配置 picgo 图床软件 ### 下载picgo [picgo下载地址](https://link.zhihu.com/?target=https%3A//github.com/Molunerfinn/PicGo/releases) 下载picgo并安装好后,按下图进行配置。仓库名为`用户/仓库`,分支为main,Token为保存的github令牌,存储路径我这里定义的是`img/`,域名为之前绑定的 Worker 服务的域名。 ![](https://pic3.zhimg.com/v2-453ab5aae132fb54e7b6009509e9ed2a_b.jpg) 保存并设为默认图床后,可以上传几张图片试一下,我这里配置的资源地址是[https://worker.digter8.com/imgs/202305011126011.png](https://link.zhihu.com/?target=https%3A//worker.digter8.com/imgs/202305011126011.png) 上传成功的图片路径应该是自己的域名+路径+文件名,如`https://worker.digter8.com/img/test.png`。 ### 替换解析地址 [https://cdn.jsdelivr.net/gh/wdsjxh/Pictures/imgs/202304301502490.png](https://link.zhihu.com/?target=https%3A//cdn.jsdelivr.net/gh/wdsjxh/Pictures/imgs/202304301502490.png) [https://worker.digter8.com/imgs/202304301502490.png](https://link.zhihu.com/?target=https%3A//worker.digter8.com/imgs/202304301502490.png) 到这里就全部完成了,愉快的白piao吧 。 标签: GitHub, Cloudflare, PicGo, Workers 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。