前言

我们知道OneDrive在国内直连速度很慢,通常只有不到1MB/S的速度,但是通过中转代理的方法可以大大提高下载速度,刚好Alist本身也提供了下载代理的功能,接下来将演示如何利用cloudflare为Alist设置OneDrive的代理。

演示效果

  • 未开启代理前

  • 开启代理后

由此可见效果很明显,可以看到下载的url也随即变动,说明正常经过了cloudflare中转服务器。

教程

准备工作

账号注册

目前由于Cloudflare Workers域名被墙,因此需要在Cloudflare拥有一个托管域名,将域名解析到workers域名解决域名被墙的问题

获取Alist账号Token

  • 进入你的Alist管理界面并登录
  • F12打开开发者控制台,点击 网络(Network)
  • Ctrl+R,随后会产生网页记录
  • 在名称中找到login,随后在右侧找到 authorization,authorization的值即为 TOKEN
  • 复制该TOKEN保留备用

已知Alist更新到2.6.4版本后,控制台不能找到login元素,但在setting中也能找到TOKEN

开始搭建

部署Cloudflare workers

  1. 登录cloudflare进入后台,左侧选择Workers选项卡,首次登录会要求创建子域(必需)。
  2. 创建服务——填写服务名称——选择启动器——创建服务。
  3. 创建好服务后,返回Workers选项卡中你会看到创建好的服务。
  4. 在Workers选项卡中,点击进入创建好的服务选择右侧的 快速编辑
  5. 在打开的编辑页面左侧填写编写好的 workers代码粘贴保存并部署即可。
    workers代码如下,若有更新请前往Alist项目参考最新代码

    const HOST = "YOUR_HOST";
    const TOKEN = "YOUR_TOKEN";
    
    addEventListener("fetch", (event) => {
        const request = event.request;
        const url = new URL(request.url);
        const sign = url.searchParams.get("sign");
        if (request.method === "OPTIONS") {
            // Handle CORS preflight requests
            event.respondWith(handleOptions(request));
        } else if (sign && sign.length === 16) {
            // Handle requests to the Down server
            event.respondWith(handleDownload(request));
        } else {
            // Handle requests to the API server
            event.respondWith(handleRequest(event));
        }
    });
    
    const corsHeaders = {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
        "Access-Control-Max-Age": "86400",
    };
    
    async function handleDownload(request) {
        const origin = request.headers.get("origin");
        const url = new URL(request.url);
        const path = decodeURI(url.pathname);
        const sign = url.searchParams.get("sign");
        const name = path.split("/").pop();
        const right = md5(`alist-${TOKEN}-${name}`).slice(8, 24);
        if (sign !== right) {
            const resp = new Response(
                JSON.stringify({
                    code: 401,
                    message: `sign mismatch`,
                }),
                {
                    headers: {
                        "content-type": "application/json;charset=UTF-8",
                    },
                }
            );
            resp.headers.set("Access-Control-Allow-Origin", origin);
            return resp;
        }
    
        let resp = await fetch(`${HOST}/api/admin/link`, {
            method: "POST",
            headers: {
                "content-type": "application/json;charset=UTF-8",
                Authorization: TOKEN,
            },
            body: JSON.stringify({
                path: path,
            }),
        });
        let res = await resp.json();
        if (res.code !== 200) {
            return new Response(JSON.stringify(res));
        }
        request = new Request(res.data.url, request);
        if (res.data.headers) {
            for (const header of res.data.headers) {
                request.headers.set(header.name, header.value);
            }
        }
        let response = await fetch(request);
    
        // Recreate the response so we can modify the headers
        response = new Response(response.body, response);
    
        // Set CORS headers
        response.headers.set("Access-Control-Allow-Origin", origin);
    
        // Append to/Add Vary header so browser will cache response correctly
        response.headers.append("Vary", "Origin");
    
        return response;
    }
    
    /**
     * Respond to the request
     * @param {Request} request
     */
    async function handleRequest(event) {
        const { request } = event;
    
        //请求头部、返回对象
        let reqHeaders = new Headers(request.headers),
            outBody,
            outStatus = 200,
            outStatusText = "OK",
            outCt = null,
            outHeaders = new Headers({
                "Access-Control-Allow-Origin": reqHeaders.get("Origin"),
                "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
                "Access-Control-Allow-Headers":
                    reqHeaders.get("Access-Control-Allow-Headers") ||
                    "Accept, Authorization, Cache-Control, Content-Type, DNT, If-Modified-Since, Keep-Alive, Origin, User-Agent, X-Requested-With, Token, x-access-token, Notion-Version",
            });
    
        try {
            //取域名第一个斜杠后的所有信息为代理链接
            let url = request.url.substr(8);
            url = decodeURIComponent(url.substr(url.indexOf("/") + 1));
    
            //需要忽略的代理
            if (
                request.method == "OPTIONS" &&
                reqHeaders.has("access-control-request-headers")
            ) {
                //输出提示
                return new Response(null, PREFLIGHT_INIT);
            } else if (
                url.length < 3 ||
                url.indexOf(".") == -1 ||
                url == "favicon.ico" ||
                url == "robots.txt"
            ) {
                return Response.redirect("https://baidu.com", 301);
            }
            //阻断
            else if (blocker.check(url)) {
                return Response.redirect("https://baidu.com", 301);
            } else {
                //补上前缀 http://
                url = url
                    .replace(/https:(\/)*/, "https://")
                    .replace(/http:(\/)*/, "http://");
                if (url.indexOf("://") == -1) {
                    url = "http://" + url;
                }
                //构建 fetch 参数
                let fp = {
                    method: request.method,
                    headers: {},
                };
    
                //保留头部其它信息
                let he = reqHeaders.entries();
                for (let h of he) {
                    if (!["content-length"].includes(h[0])) {
                        fp.headers[h[0]] = h[1];
                    }
                }
                // 是否带 body
                if (["POST", "PUT", "PATCH", "DELETE"].indexOf(request.method) >= 0) {
                    const ct = (reqHeaders.get("content-type") || "").toLowerCase();
                    if (ct.includes("application/json")) {
                        let requestJSON = await request.json();
                        console.log(typeof requestJSON);
                        fp.body = JSON.stringify(requestJSON);
                    } else if (
                        ct.includes("application/text") ||
                        ct.includes("text/html")
                    ) {
                        fp.body = await request.text();
                    } else if (ct.includes("form")) {
                        // fp.body = await request.formData();
                        fp.body = await request.text();
                    } else {
                        fp.body = await request.blob();
                    }
                }
                // 发起 fetch
                let fr = await fetch(url, fp);
                outCt = fr.headers.get("content-type");
                if (outCt.includes("application/text") || outCt.includes("text/html")) {
                    try {
                        // 添加base
                        let newFr = new HTMLRewriter()
                            .on("head", {
                                element(element) {
                                    element.prepend(`<base href="${url}" />`, {
                                        html: true,
                                    });
                                },
                            })
                            .transform(fr);
                        fr = newFr;
                    } catch (e) {}
                }
                outStatus = fr.status;
                outStatusText = fr.statusText;
                outBody = fr.body;
            }
        } catch (err) {
            outCt = "application/json";
            outBody = JSON.stringify({
                code: -1,
                msg: JSON.stringify(err.stack) || err,
            });
        }
    
        //设置类型
        if (outCt && outCt != "") {
            outHeaders.set("content-type", outCt);
        }
    
        let response = new Response(outBody, {
            status: outStatus,
            statusText: outStatusText,
            headers: outHeaders,
        });
    
        return response;
    }
    
    const blocker = {
        keys: [],
        check: function (url) {
            url = url.toLowerCase();
            let len = blocker.keys.filter((x) => url.includes(x)).length;
            return len != 0;
        },
    };
    
    function handleOptions(request) {
        // Make sure the necessary headers are present
        // for this to be a valid pre-flight request
        let headers = request.headers;
        if (
            headers.get("Origin") !== null &&
            headers.get("Access-Control-Request-Method") !== null
            // && headers.get("Access-Control-Request-Headers") !== null
        ) {
            // Handle CORS pre-flight request.
            // If you want to check or reject the requested method + headers
            // you can do that here.
            let respHeaders = {
                ...corsHeaders,
                // Allow all future content Request headers to go back to browser
                // such as Authorization (Bearer) or X-Client-Name-Version
                "Access-Control-Allow-Headers": request.headers.get(
                    "Access-Control-Request-Headers"
                ),
            };
    
            return new Response(null, {
                headers: respHeaders,
            });
        } else {
            // Handle standard OPTIONS request.
            // If you want to allow other HTTP Methods, you can do that here.
            return new Response(null, {
                headers: {
                    Allow: "GET, HEAD, POST, OPTIONS",
                },
            });
        }
    }
    
    !(function (a) {
        "use strict";
        function b(a, b) {
            var c = (65535 & a) + (65535 & b),
                d = (a >> 16) + (b >> 16) + (c >> 16);
            return (d << 16) | (65535 & c);
        }
        function c(a, b) {
            return (a << b) | (a >>> (32 - b));
        }
        function d(a, d, e, f, g, h) {
            return b(c(b(b(d, a), b(f, h)), g), e);
        }
        function e(a, b, c, e, f, g, h) {
            return d((b & c) | (~b & e), a, b, f, g, h);
        }
        function f(a, b, c, e, f, g, h) {
            return d((b & e) | (c & ~e), a, b, f, g, h);
        }
        function g(a, b, c, e, f, g, h) {
            return d(b ^ c ^ e, a, b, f, g, h);
        }
        function h(a, b, c, e, f, g, h) {
            return d(c ^ (b | ~e), a, b, f, g, h);
        }
        function i(a, c) {
            (a[c >> 5] |= 128 << c % 32), (a[(((c + 64) >>> 9) << 4) + 14] = c);
            var d,
                i,
                j,
                k,
                l,
                m = 1732584193,
                n = -271733879,
                o = -1732584194,
                p = 271733878;
            for (d = 0; d < a.length; d += 16)
                (i = m),
                    (j = n),
                    (k = o),
                    (l = p),
                    (m = e(m, n, o, p, a[d], 7, -680876936)),
                    (p = e(p, m, n, o, a[d + 1], 12, -389564586)),
                    (o = e(o, p, m, n, a[d + 2], 17, 606105819)),
                    (n = e(n, o, p, m, a[d + 3], 22, -1044525330)),
                    (m = e(m, n, o, p, a[d + 4], 7, -176418897)),
                    (p = e(p, m, n, o, a[d + 5], 12, 1200080426)),
                    (o = e(o, p, m, n, a[d + 6], 17, -1473231341)),
                    (n = e(n, o, p, m, a[d + 7], 22, -45705983)),
                    (m = e(m, n, o, p, a[d + 8], 7, 1770035416)),
                    (p = e(p, m, n, o, a[d + 9], 12, -1958414417)),
                    (o = e(o, p, m, n, a[d + 10], 17, -42063)),
                    (n = e(n, o, p, m, a[d + 11], 22, -1990404162)),
                    (m = e(m, n, o, p, a[d + 12], 7, 1804603682)),
                    (p = e(p, m, n, o, a[d + 13], 12, -40341101)),
                    (o = e(o, p, m, n, a[d + 14], 17, -1502002290)),
                    (n = e(n, o, p, m, a[d + 15], 22, 1236535329)),
                    (m = f(m, n, o, p, a[d + 1], 5, -165796510)),
                    (p = f(p, m, n, o, a[d + 6], 9, -1069501632)),
                    (o = f(o, p, m, n, a[d + 11], 14, 643717713)),
                    (n = f(n, o, p, m, a[d], 20, -373897302)),
                    (m = f(m, n, o, p, a[d + 5], 5, -701558691)),
                    (p = f(p, m, n, o, a[d + 10], 9, 38016083)),
                    (o = f(o, p, m, n, a[d + 15], 14, -660478335)),
                    (n = f(n, o, p, m, a[d + 4], 20, -405537848)),
                    (m = f(m, n, o, p, a[d + 9], 5, 568446438)),
                    (p = f(p, m, n, o, a[d + 14], 9, -1019803690)),
                    (o = f(o, p, m, n, a[d + 3], 14, -187363961)),
                    (n = f(n, o, p, m, a[d + 8], 20, 1163531501)),
                    (m = f(m, n, o, p, a[d + 13], 5, -1444681467)),
                    (p = f(p, m, n, o, a[d + 2], 9, -51403784)),
                    (o = f(o, p, m, n, a[d + 7], 14, 1735328473)),
                    (n = f(n, o, p, m, a[d + 12], 20, -1926607734)),
                    (m = g(m, n, o, p, a[d + 5], 4, -378558)),
                    (p = g(p, m, n, o, a[d + 8], 11, -2022574463)),
                    (o = g(o, p, m, n, a[d + 11], 16, 1839030562)),
                    (n = g(n, o, p, m, a[d + 14], 23, -35309556)),
                    (m = g(m, n, o, p, a[d + 1], 4, -1530992060)),
                    (p = g(p, m, n, o, a[d + 4], 11, 1272893353)),
                    (o = g(o, p, m, n, a[d + 7], 16, -155497632)),
                    (n = g(n, o, p, m, a[d + 10], 23, -1094730640)),
                    (m = g(m, n, o, p, a[d + 13], 4, 681279174)),
                    (p = g(p, m, n, o, a[d], 11, -358537222)),
                    (o = g(o, p, m, n, a[d + 3], 16, -722521979)),
                    (n = g(n, o, p, m, a[d + 6], 23, 76029189)),
                    (m = g(m, n, o, p, a[d + 9], 4, -640364487)),
                    (p = g(p, m, n, o, a[d + 12], 11, -421815835)),
                    (o = g(o, p, m, n, a[d + 15], 16, 530742520)),
                    (n = g(n, o, p, m, a[d + 2], 23, -995338651)),
                    (m = h(m, n, o, p, a[d], 6, -198630844)),
                    (p = h(p, m, n, o, a[d + 7], 10, 1126891415)),
                    (o = h(o, p, m, n, a[d + 14], 15, -1416354905)),
                    (n = h(n, o, p, m, a[d + 5], 21, -57434055)),
                    (m = h(m, n, o, p, a[d + 12], 6, 1700485571)),
                    (p = h(p, m, n, o, a[d + 3], 10, -1894986606)),
                    (o = h(o, p, m, n, a[d + 10], 15, -1051523)),
                    (n = h(n, o, p, m, a[d + 1], 21, -2054922799)),
                    (m = h(m, n, o, p, a[d + 8], 6, 1873313359)),
                    (p = h(p, m, n, o, a[d + 15], 10, -30611744)),
                    (o = h(o, p, m, n, a[d + 6], 15, -1560198380)),
                    (n = h(n, o, p, m, a[d + 13], 21, 1309151649)),
                    (m = h(m, n, o, p, a[d + 4], 6, -145523070)),
                    (p = h(p, m, n, o, a[d + 11], 10, -1120210379)),
                    (o = h(o, p, m, n, a[d + 2], 15, 718787259)),
                    (n = h(n, o, p, m, a[d + 9], 21, -343485551)),
                    (m = b(m, i)),
                    (n = b(n, j)),
                    (o = b(o, k)),
                    (p = b(p, l));
            return [m, n, o, p];
        }
        function j(a) {
            var b,
                c = "";
            for (b = 0; b < 32 * a.length; b += 8)
                c += String.fromCharCode((a[b >> 5] >>> b % 32) & 255);
            return c;
        }
        function k(a) {
            var b,
                c = [];
            for (c[(a.length >> 2) - 1] = void 0, b = 0; b < c.length; b += 1) c[b] = 0;
            for (b = 0; b < 8 * a.length; b += 8)
                c[b >> 5] |= (255 & a.charCodeAt(b / 8)) << b % 32;
            return c;
        }
        function l(a) {
            return j(i(k(a), 8 * a.length));
        }
        function m(a, b) {
            var c,
                d,
                e = k(a),
                f = [],
                g = [];
            for (
                f[15] = g[15] = void 0, e.length > 16 && (e = i(e, 8 * a.length)), c = 0;
                16 > c;
                c += 1
            )
                (f[c] = 909522486 ^ e[c]), (g[c] = 1549556828 ^ e[c]);
            return (d = i(f.concat(k(b)), 512 + 8 * b.length)), j(i(g.concat(d), 640));
        }
        function n(a) {
            var b,
                c,
                d = "0123456789abcdef",
                e = "";
            for (c = 0; c < a.length; c += 1)
                (b = a.charCodeAt(c)), (e += d.charAt((b >>> 4) & 15) + d.charAt(15 & b));
            return e;
        }
        function o(a) {
            return unescape(encodeURIComponent(a));
        }
        function p(a) {
            return l(o(a));
        }
        function q(a) {
            return n(p(a));
        }
        function r(a, b) {
            return m(o(a), o(b));
        }
        function s(a, b) {
            return n(r(a, b));
        }
        function t(a, b, c) {
            return b ? (c ? r(b, a) : s(b, a)) : c ? p(a) : q(a);
        }
        "function" == typeof define && define.amd
            ? define(function () {
                return t;
            })
            : (a.md5 = t);
    })(this);

代码说明

  • YOUR_HOST改为你的Alist地址,必须添加协议头,且后面不能添加/。如https://alist.itleaf.xyz
  • YOUR_TOKEN改为Alist账号的 Token

示意图

随后点击保存并部署即可进入下一步

域名解析

上文谈到Cloudflare workers域名被墙,所以我们需要在Cloudflare拥有一个托管域名,域名请自行购买,便宜的也就几块钱1年,因为我们主要用来搞代理,域名就不用那么好了,或者你也可以将其它域名注册商的域名转接到Cloudflare

  • 我们点击左侧DNS管理域名解析
  • 添加CNAME类型的解析,主机名自定,内容填写你创建的workers域名,打开黄色小云朵,保存即可
    务必打开黄色小云朵,否则无效!!!
  • 接下来我们回到Workers界面,点击 触发器-添加路由
  • 路由填写你刚刚解析的 域名加上/* ,区域选择你的域名,点击添加路由即可

Alist代理设置

进入Alist管理后台,找到你挂载OneDrive的设置,打开 代理,并在 down_proxy_url填入你刚刚解析的域名,必须要有https://前缀

随后点击保存。接下来下载OneDrive的文件就会经过clouflare workers代理了。

总结

  • 由于Cloudflare workers域名被墙,给代理中转增加了很多麻烦,因此需要大家自行准备域名
  • 下载代理中转只会在你设置代理的挂载路径下生效
  • 下载代理中转同样可用于其它网盘,但部分网盘检测到cloudflare多ip下载可能会封号!
最后修改:2022 年 11 月 12 日
如果觉得我的文章对你有用,请随意赞赏