ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

LinkSwift 网盘直链助手实战:跨域请求管线、直链批量获取与请求取消怎么做的

LinkSwift 网盘直链助手实战:跨域请求管线、直链批量获取与请求取消怎么做的 LinkSwift 网盘直链助手实战跨域请求管线、直链批量获取与请求取消怎么做的【免费下载链接】Online-disk-direct-link-download-assistant一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 夸克网盘 / UC网盘 / 123云盘 八大网盘项目地址: https://gitcode.com/GitHub_Trending/on/Online-disk-direct-link-download-assistant写过油猴脚本的人都卡过同一道坎内容脚本里发起的跨域 XHR 会被 CORS 拦下。LinkSwift网盘直链下载助手要同时打通百度网盘、阿里云盘、123 云盘等九套 API它把跨域请求收敛到单点入口用一套头标准化管线、直链批量缓存和可取消的 Promise 解决全部问题。若需要对照阅读完整实现可克隆仓库后从主脚本入手git clone https://gitcode.com/GitHub_Trending/on/Online-disk-direct-link-download-assistant默认请求管线所有跨域请求只走一个入口本节回答脚本凭什么能在pan.baidu.com页面上请求openapi.baidu.com的接口内容脚本运行在页面上下文里原生XMLHttpRequest/fetch对跨域请求受 CORS 约束而各大网盘的直链 API 并不会给浏览器发Access-Control-Allow-Origin。油猴生态的解法是GM_xmlhttpRequest——它由脚本管理器代理发起天然绕过同源策略。LinkSwift 在 改网盘直链下载助手.user.js 第 931 行把它收敛为唯一出口xmlHttpRequest(option) { const xmlHttpRequest (typeof GM_xmlhttpRequest function) ? GM_xmlhttpRequest : (typeof GM?.xmlHttpRequest function) ? GM.xmlHttpRequest : null; if (!xmlHttpRequest || base.isType(xmlHttpRequest) ! function) throw new Error(GreaseMonkey 兼容 XMLHttpRequest 不可用。); return xmlHttpRequest({ withCredentials: true, ...option }); },先探测全局GM_xmlhttpRequest取不到再尝试GM.xmlHttpRequest命名空间写法适配不同脚本管理器暴露 API 的方式差异找不到兼容实现直接抛错——快速失败比带着残缺功能运行更安全避免后续几十个请求逐个报网络错误{ withCredentials: true, ...option }的展开顺序是关键调用方的参数写在后面可以覆盖除凭证外的选项但withCredentials无法被关掉——所有跨域请求一律携带 Cookie这是不变式网盘接口的鉴权基础就在这。请求头的默认值与覆盖规则base.get/base.post在发请求前都会先过一遍standHeaders第 866 行return withServer ? { Accept: */*, User-Agent: navigator.userAgent, ...(withOrigin ? { Origin: location.origin, Referer: ${location.origin}/ } : {}), ...newHeaders } : newHeaders;键名统一转大驼峰content-type→Content-Type同名键不覆盖而是合并成数组避免调试时同名头被悄悄吞掉Accept、User-Agent、Origin、Referer由管线自动补齐调用方只传差异部分...newHeaders放在展开的最后一位调用方头永远压过默认头——这条规则后面覆盖 UA 时还会再用到。响应侧同样有默认行为。post的onload第 978-989 行对json类型做了四级递进解码if (type json) { res.responseDecode res.responseText; try { res.responseDecode atob(res.responseDecode) } catch { } try { res.responseDecode escape(res.responseDecode) } catch { } try { res.responseDecode decodeURIComponent(res.responseDecode) } catch { } try { res.responseDecode JSON.parse(res.responseDecode) } catch { } if (res.responseDecode res.responseText) res.responseDecode null; if (this.isType(res.response) object) res.responseDecode res.response; } resolve(full ? res : res.responseDecode ?? res.response ?? res.responseText);按atob → escape → decodeURIComponent → JSON.parse的顺序逐级尝试每步失败都静默跳到下一步——因为百度部分接口会把 JSON 再包一层 base64不同接口编码层数不同解码结果与原文相同时置null表示没解出任何东西走后面的??兜底返回值优先级是responseDecode ?? response ?? responseText调用方拿到的可能是对象、也可能是原始字符串这是后文要列的坑之一type blob时跳过整条解码链直接 resolve增强下载分片聚合就依赖这个分支。一次跨域 API 调用拿百度直链的最小链路本节给出一段真实发生过的取链调用看管线各层如何配合。来源同文件$baidu.getFilesUrl第 6140-6156 行节选const url new URL(config.$baidu.api.getLink); url.searchParams.set(fsids, fsids); url.searchParams.set(access_token, token); const res await base.get(url, { User-Agent: config.$baidu.api.ua.downloadLink }); if (!res || res.errno ! 0 || !res.list) { if (res.errno 112) throw new Error(提示br/页面已过期请刷新后重试~br/代码 res.errno); if (res.errno 9019) { base.delValue(baidu_access_token); throw new Error(提示br/访问令牌已过期再获取一次吧~br/代码 res.errno); } ... }接口地址不写死在业务代码里而是从第 218 行的config.$baidu.api.getLink即rest/2.0/xpan/multimedia?methodfilemetasdlink1取出new URLsearchParams构造查询串免去手动 encodeURIComponent第二个参数传了{ User-Agent: pan.baidu.com }百度 CDN 会校验该请求头决定是否吐出有效dlink而standHeaders里调用方参数展开在最后这一步是覆盖管线默认头而非追加errno 9019表示 access_token 过期此时调用base.delValue(baidu_access_token)清掉本地令牌下一次运行会自动走 OAuth 授权页重新拿 token——清缓存是唯一的自愈路径errno 112对应页面会话过期直接抛错引导刷新不做自动重试。直链批量获取50 个一批 4 小时缓存本节解决一次勾选几百个文件时的取链效率与风控问题。同一函数的前半段第 6122-6177 行节选const size 50; ... for (let i 0; i pending.length; i size) { // 当前批次 const batch pending.slice(i, i size); const fsids JSON.stringify(batch.map(item item.fs_id)); const url new URL(config.$baidu.api.getLink); url.searchParams.set(fsids, fsids); url.searchParams.set(access_token, token); const res await base.get(url, { User-Agent: config.$baidu.api.ua.downloadLink }); ... batch.forEach(_item { const item res.list.find(li li.fs_id _item.fs_id); if (item) { Object.assign(_item, item); temp.glinks.push({ id: _item.fs_id, expires: (Date.now() 4 * 60 * 60 * 1000), data: item }); } }); ... // 批次间休息 if (i size pending.length) await base.sleep(1000); }size 50百度的dlink1接口支持一次传多个 fs_id脚本按 50 个一批拆分控制单请求体积的同时把 N 次请求压成 N/50 次批次之间sleep(1000)是刻意的限流间隔降低短时间密集请求触发风控的概率取到的直链写入temp.glinks条目结构为{ id: fs_id, expires: Date.now() 4*60*60*1000, data }——缓存 4 小时请求发出前先过一遍缓存第 6125-6138 行const pending items.filter(item { const idx temp.glinks.findIndex(c c.id item.fs_id); if (idx -1) return true; const cached temp.glinks[idx]; if (cached.expires Date.now()) { Object.assign(item, cached.data); proc; return false; } temp.glinks.splice(idx, 1); // 过期删除 return true; });命中且未过期就Object.assign回填、跳过请求过期条目从缓存中删掉再进待办。缓存键用文件身份fs_id而不是 URL——网盘直链本身带时效参数用 URL 做键会永远 miss。一句话收口直链的生命周期归缓存管请求只为未命中付费。请求取消与 HEAD 降级登记表 会退路的探测本节解决两个工程细节在途请求怎么取消、直链可用性怎么低成本探测。Promise 上的 abort第 999-1003 行post/get在返回 Promise 前做了一件不太常见的事if (request) { const methods Object.getOwnPropertyNames(request).filter(key typeof request[key] function !promise.hasOwnProperty(key) ![then, catch, finally].includes(key)); methods.forEach(method { promise[method] (...args) requestmethod; }); } return promise;遍历底层 request 对象上的函数属性abort在内排除 Promise 原生方法后动态绑定到 Promise 上——promise.abort()可以直接调无需额外保存句柄每个带编号的请求创建时会注册进temp.request[index]第 1475 行取消逻辑因此变成查表调用_resetAllData()遍历登记表逐个abort()再清空_resetData(i)只撤单个第 1558-1572 行。不变式取消走登记表索引而不是销毁 Promise——业务句柄与 Promise 是同一个对象但谁活着由temp.request说了算。HEAD 取不到头时的降级base.head第 1071 行起用于探测直链可用性它内置了退路// 如果没用 GET 没回 Range 非成功返回 -- 用 HEADERS_RECEIVED 适配 if (!usingGET !res.responseHeaders.hasOwnProperty(Range) !(res.status 200 res.status 400)) { base.head(res.finalUrl, { ...headers, Range: bytes0-0 }, true).then(resolve).catch(reject); return; }判定条件是三者同时成立当前不是 GET、响应头里没有Range回显、状态码非 2xx/3xx——即HEAD 被服务端明确不配合命中后改发GET Range: bytes0-0重试且onreadystatechange里readyState 2HEADERS_RECEIVED时立刻abort()只取响应头不拉正文探测成本几乎为零。常见坑errno 9019、50MB 直链限制与静默解码⚠️errno 9019 必须伴随清 token。只弹提示不清baidu_access_token用户会陷入提示过期 → 刷新 → 仍过期的死循环。源码的处理是base.delValue(baidu_access_token)后抛错第 6154 行token 的重新获取依赖main.init()里对openapi.baidu.com/oauth页面的分支识别第 9021 行temp.main $baiduAuthorizefork 后若删掉这条匹配自愈链就断了。百度直链对访问方式有额外限制config.$baidu.dom.copy明确写着直接访问超过 50MB 文件的直链会导致服务器回报 403 错误如需访问请修改用户代理(UA)为 pan.baidu.com。这也是取链请求必须覆盖 UA 的另一个原因。⚠️withCredentials: true无法关闭。base.xmlHttpRequest强制所有请求携带 Cookie见默认请求管线一节。这是网盘接口的必要认证但也意味着脚本发出的每个跨域请求都在暴露登录态——改造时务必保证请求目标只指向各网盘自有域名connect白名单脚本头第 64-87 行就是为此准备的护栏。⚠️解码链是尽力而为的。post里每一步都包着空catch {}响应既不是 base64 也不是合法 JSON 时最终原样返回responseText字符串而不是对象。调用方判断res.errno之前要容忍拿到的是字符串的分支——源码里if (!res || res.errno ! 0 ...)这种写法正是为此。速查表操作效果适用场景代价/注意base.xmlHttpRequest(option)跨域请求强制withCredentials: true一切需要携带登录态的 API 调用凭证无法关闭目标域名须在connect内base.get/post(url, headers)头自动标准化大驼峰 默认 Origin/Refererjson 响应走 base64→escape→URI→JSON 解码链网盘直链/文件列表接口返回值可能是对象也可能是字符串失败静默降级调用方传{User-Agent: pan.baidu.com}覆盖管线默认 UA百度 CDN 校验 UA 的接口只影响该次请求不会改写全局默认头百度直链批量取50 个 fs_id 一批 sleep(1000)间隔结果缓存 4 小时键为 fs_id大文件列表取链缓存过期条目自动删除重取errno 9019 需清 tokentemp.request[i].abort()/_resetAllData()按索引取消单个或多个在途请求用户点取消、切换文件列表依赖创建时的注册未注册的请求撤不掉base.head(url, headers)HEAD 探测服务端不配合时自动降级为GET Range: bytes0-0readyState2即中断直链可用性校验只取响应头不产生正文流量这套单点入口 头标准化 可取消 Promise的请求层正是 LinkSwift 能用同一套业务代码覆盖九大网盘的底座。若你想给自己的脚本加一套同类能力从xmlHttpRequest的三行封装和temp.request登记表抄起就是最短路径。【免费下载链接】Online-disk-direct-link-download-assistant一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 夸克网盘 / UC网盘 / 123云盘 八大网盘项目地址: https://gitcode.com/GitHub_Trending/on/Online-disk-direct-link-download-assistant创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表