NodeJS使用Range请求实现下载功能的方法示例

2020-06-17 05:35:17易采站长站整理

在通过 url 和 path 模块解析和拼接下载文件路径时,应该进行错误检测,如果文件不存在则直接返回客户端 Not Found。

我们可以使用 curl 命令来检测我们的服务端代码,在命令行工具中输入下面命令,在命令窗口查看返回值是否正确。


curl -v --header "Range:bytes=0-5" http://localhost:3000

客户端的实现

在上面使用 curl 命令来访问我们的服务器时,只能请求固定范围的数据,而不是类似于下载功能,每次都下载一个范围的数据,但是想要多次下载并自动维护 Range 的范围需要借助我们自己实现的客户端逻辑。

为了简便,我们的下载客户端是在命令行窗口运行的,通过指令来模拟实际项目中的开始下载、暂停和恢复按钮,当在窗口中输入 s 指令时开始下载,输入 p 指令时暂停下载,输入 r 指令时恢复下载。


// 文件:client.js
const http = require("http");
const fs = require("fs");
const path = require("path");

// 请求配置
let config = {
host: "localhost",
port: 3000,
path: "/download.txt"
};

let start = 0; // 请求初始值
let step = 5; // 每次请求字符个数
let pause = false; // 暂停状态
let total; // 文件总长度

// 创建可写流
let ws = fs.createWriteStream(path.resolve(__dirname, config.path.slice(1)));

// 下载函数
function download() {
// 配置,每次范围请求 step 个字节
config.headers = {
"Range": `bytes=${start}-${start + step - 1}`;
};

// 维护下次 start 的值
start += step;

// 发送请求
http.request(config, res => {
// 获取文件总长度
if (typeof total !== "number") {
total = res.headers["content-ranges"].match(//(d*)/)[1];

}

// 读取返回数据
let buffers = [];
res.on("data", data => buffers.push(data));
res.on("end", () => {
// 合并数据并写入文件
let buf = Buffer.concat(buffers);
ws.write(buf);

// 递归进行下一次请求
if (!pause && start < total) {
download();
}
});
}).end();
}

// 监控输入
process.stdin.on("data", data => {
// 获取指令
let ins = data.toString().match(/(w*)/r/)[1];
switch (ins) {
case "s":
case "r":
pause = false;
download();
break;
case "p":
pause = true;
break;
}
});

在上面代码中下载的文件通过 config 中的 path 属性配置,每次调用 download 函数下载时都会重新计算当前范围请求的初始位置和结束位置,并设置 Range 请求头,下一次请求靠递归 download 来实现。