本帖最后由 steven026 于 2023-8-21 12:46 编辑
前言
环境
本编指南内容并非纯前端指南,不仅涉及前端脚本猫/油猴脚本,且涉及后端本地服务器。
本编指南以前端脚本猫、后端Node.js为例,其余前后端原理相通大同小异,自行转换。
背景
众所周知,现代前端浏览器出于安全的目的,禁止网页、脚本、插件直接静默下载文件到本地,必须经过用户手动确认。
但是当我们一次性下载较多或较频繁下载文件时,每次都要单独手动确认实在过于麻烦。
即使使用JSZip打包文件也只是减少了确认次数,仍需手动确认。
为了解决静默下载的需求,我们可以引入后端本地服务器,前端静默下载属于越权行为,但在后端并无限制,且只是单纯为了静默下载只需搭建一个简易后端服务器即可,非常方便。
既然纯前端静默下载属于越权行为无法做到,那么我们为什么不直接用纯后端环境而是使用前后端相结合的方法呢?
由于纯后端缺少浏览器环境无法轻易下载所有文件,部分文件需要登录、验证、鉴权方可下载,但并非所有登录验证鉴权都可以轻易做到,更多的是需要逆向网站计算出其加密方法,而这一过程实在过于麻烦,不如使用前后端相结合的方式,前端模拟正常用户操作负责正常登录验证下载,而后端负责写入到本地。
原理
前端(脚本猫)负责获取文件下载链接并使用GM_xmlhttpRequest
(非GM_download
)以responseType: "blob"
形式下载到本地,并通过GM_xmlhttpRequest
直接将blob POST到本地后端服务器
后端(Node.js) 负责搭建本地服务器接收前端请求,通过Buffer
解析请求内容并写入到本地
建议阅读
前端API(脚本猫)
GM_xmlhttpRequest
https://docs.scriptcat.org/docs/dev/api/#gm_xmlhttprequest-
后端API(Node.js)
Http http.createServer/request
https://nodejs.org/dist/latest-v20.x/docs/api/http.html
File System fs.writeSync
https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#fswritesyncfd-buffer-options
可选指南
[油猴脚本开发指南]实现前端全自动唤醒本地后端
https://bbs.tampermonkey.net.cn/thread-4531-1-1.html
代码
以下载脚本猫图标为例,
(GM_xmlhttpRequest可自动填充浏览器环境参数,如无特殊Headers无需填写额外内容)
脚本猫代码
GM_xmlhttpRequest({
// 希望下载的文件路径
url: 'https://bbs.tampermonkey.net.cn/data/attachment/common/a3/common_68_icon.png',
method: 'GET',
// 以blob形式接收文件,可直接转发给后端无需额外操作
responseType: 'blob',
onload: (xhr) => {
console.log(xhr)
if (xhr.status != 200) throw new Error('response error')
// 将下载到的blob直接转发给后端
GM_xmlhttpRequest({
// 端口与后端保持相同 路径为希望文件存放路径
url: "http://localhost:1234/test/png/scriptcat.png",
method: 'POST',
data: xhr.response,
onload: (xhr_) => console.log(xhr_)
})
}
})
Node.js代码
const http = require('http');
const fs = require('fs');
// 监听端口
const port = '1234';
// 文件保存目录
const dataDir = 'D:';
http.createServer(async function (req, res) {
console.log(req);
if (req.url == '/' || req.method!='POST') return res.end('invaild path or method');
// 根据请求url确定文件保存路径
const fileDir = dataDir + req.url;
const data = [];
req.on('data', (chunk) => data.push(chunk));
req.on('end', () => {
const file = Buffer.concat(data);
// 写入文件
fs.writeFileSync(fileDir, file);
});
res.end('success ' + fileDir);
}).listen(port);
效果预览
全程静默下载,无需额外确认。
完结撒花
咕咕咕