提取器:主要功能是将输入的微博视频地址转换成可下载的视频地址
app.js全部代码
const express = require("express");
const http = require("http")
const https = require("https");
const path = require("path");
var iconv = require("iconv-lite");
const app = express();
//设置静态资源根目录
app.use(express.static(path.join(__dirname, "public")));
// 拦截到所有的请求
app.all("*", function (req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
//允许的header类型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == "options")
res.send(200); //让options尝试请求快速结束
else
next();
})
// 作为服务器接受来自浏览器的get请求
app.get("/getVideo", (req, res) => {
// 微博视频来源
if (!!req.query.videourl && req.query.videourl.startsWith('http://t.cn/')) {
http.get(req.query.videourl, firstResponse => {
const { statusCode } = firstResponse;
if (statusCode == 302) {
const { location } = firstResponse.headers;
// 作为客户端向【微博服务器】发送http请求,方式为get(第二次)
https.get(location, secondResponse => {
const { statusCode } = secondResponse;
if (statusCode == 302) {
const { location } = secondResponse.headers;
const startIndex = location.indexOf('//') + 2;
const endIndex = location.indexOf('/', startIndex);
// 作为客户端向【微博服务器】发送http请求,方式为get(第三次)
const httpreq = https.get({
host: location.substring(startIndex, endIndex),
port: 443,
path: location.substr(endIndex),
headers: {
// "Cookie": "SINAGLOBAL=3449239060440.6143.1571564937170; wvr=6; SUB=_2AkMpZuetf8NxqwJRmfgXxW_iZYh_yQDEieKfOhZ2JRMxHRl-yT9kqmoPtRB6AubJQgewqj9OLHJL43IGKJgFf_2WxcuS; SUBP=0033WrSXqPxfM72-Ws9jqgMF55529P9D9WFH5qiDNhwszhEYmWXn51ix; YF-V5-G0=f0aacce81fff76e1515ae68ac76a20c3; _s_tentry=passport.weibo.com; Apache=9408231403115.021.1580886172357; ULV=1580886172407:1:1:1:9408231403115.021.1580886172357:; WBStorage=42212210b087ca50|undefined; YF-Page-G0=d30fd7265234f674761ebc75febc3a9f|1580886234|1580886169"
}
}, response => {
response.on('data', (chunk) => {
// 检查微博服务器返回的字符串是否有包含视频的相关标记
if (chunk.toString().includes('video-sources')) {
chunk = chunk.toString();
const startIndex = chunk.indexOf('fluency=') + 8;
const endIndex = chunk.indexOf(' ', startIndex) - 1;
chunk = chunk.substring(startIndex, endIndex);
chunk = unescape(chunk)
res.send(chunk)
return;
}
});
})
httpreq.on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
}
})
}
})
}
})
// 根据不同的二级域名分别指向不同的静态目录
app.get('/', (req, res) => {
// 获取URL中的主机信息
let hostname = req.headers.host;
hostname = hostname.substr(0, hostname.indexOf('.'));
res.sendFile(path.join(__dirname, 'public', hostname, 'index.html'));
})
const ip = "127.0.0.1";
app.listen(80, ip, err => {
if (err) throw err;
console.log("启动成功!端口为80,IP地址:" + ip);
})
主要代码解读
代码行76--81
这段代码可以检测地址栏中的二级域名,然后根据不同的二级域名请求,将对应的后台目录中的index.html文件响应给前端。比喻,我们在前端请求test.mydomail.com时,后台可以拿到test标识,然后将后台工程目录中的test目录下的index.html返回给前端,这相当于test目录就是前端的主目录。这样,可以用不同的二级域名标识不同的前端工程项目。
代码行9
这段代码很重要, 因为前端拿到index.html文件后必然要在浏览器中加载进来, 这个加载的过程其实就是解析HTM标签的过程, 当浏览器遇到形如
<link href=video_extractor/assets/css/app.029c5e71.css rel=stylesheet>
<script src=video_extractor/assets/js/chunk-vendors.f918d654.js> </script>
等标签时必定再次向后台发送http请求, Node.js默认情况下不会处理类似的静态文件请求,除非声明如行9的代码.
至此,前端的第一次请求宣告完成,同时也意味着首页完成加载,等待用户的输入或点击
代码行26
app.get("/getVideo", (req, res) => {}) 表示其作为后台服务器端接受来自/getVideo路由(路径)的请求,req表示请求对象, res表示响应对象。
其中req有几个属性很重要,如query表示URL中?号后面的部分;
代码行30、35、42
http(s).get(url, req=>{})表示其作为客户端(也就是http请求端,典型如浏览器)向其他的后台服务器发送http请求,就好比某个后台服务并没有前端所请求的数据,但它可以向其他的后台请求数据,功能类似于代理、中介等,然后再返回给前端。