有时候下载文件下载到一半的时候网络断开了, 需要继续下载的时候, 如果不支持断点, 用户需要重新下载, 如果支持 range, 客户端会记录已经下载的内容,当网络恢复时, 则向服务端发送剩下 range 的读取请求, 服务端接收到请求后,会根据 range 找到剩下的内容 发送给 客户端。
客户端也可以通过 curl -v --header 'Range: bytes=xx-xx' http://localhost:3000 来指定资源的 byte 范围, 但是具体怎么返回还是服务端说的算, 从什么位置开始返回 也是服务端说的算 可以是 0 也可以是 100 server 通过请求头获取 Range: bytes=xx-xx 来判断是否支持 range , 如果这个值存在 且 有效,通过 res.setHeader('Accept-Ranges','bytes');res.setHeader('Content-Range','${start}-${end}/${total}') 告知客户端支持 range , 每次发送内容的 bytes 范围, 并将对应的内容发送给 客户端 ,响应的状态码变成 206,表示 Partial Content,并设置 Content-Range。如果无效,则返回 416 状态码,表明 Request Range Not 客户端 通过 response.headers['content-range'] 可以获取 range 范围 及 总数, 以便客户端知道什么时候该结束请求 客户端代码
let options = { hostname: 'localhost', port: 3000, path: '/', method: 'GET' };
let fs = require('fs'); let path = require('path'); let http = require('http'); let ws = fs.createWriteStream('./download.txt'); let pause = false; // 是否暂停 let start = 0; // 开始位置
// 下载 = 每次获取 10 个下载 process.stdin.on('data', function (chunk) { chunk = chunk.toString(); if (chunk.includes('p')) { // 断开下载 pause = true; } else { // 继续下载 pause = false; download(); } });
function download () { options.headers = { Range: bytes=${start}-${start + 10}
} start += 10; // 发送请求 http.get(options, function (res) { let range = res.headers['content-range']; let total = range.split('/')[1]; let buffers = []; res.on('data', function (chunk) { buffers.push(chunk); }); res.on('end', function () { ws.write(Buffer.concat(buffers)); // 将 buffers 转为字符串写入到文件中 setTimeout(function () { if (pause === false && start < total) { download(); } }, 1000) }) }) }
download();
服务端
let http = require('http'); let fs = require('fs'); let path = require('path'); let { promisify } = require('util'); let stat = promisify(fs.stat);
/** * 客户端会发送一个头 Range: bytes=0-10 * 服务端返回一个头 * Accept-Ranges: bytes * Content-Range: 0-10/总大小 */ let serevr = http.createServer(async function (req, res) { let p = path.join(__dirname, './content.txt'); let statObj = await stat(p); let start = 0; let end = statObj.size - 1; // 读流是包前又包后的 let total = end; let range = req.headers['range'];
if (range) {
res.setHeader('Accept-Ranges', 'bytes');
let result = range.match(/bytes=(\d*)-(\d*)/);
start = result[1] ? parseInt(result[1]) : start;
end = result[2] ? parseInt(result[2]) - 1 : end; // 因为流的 end 是 包前又包后的 此次这个地方需要减去 1
// 告知客户端获取成功
res.setHeader('Content-Range', `${start}-${end}/${total}`);
}
res.setHeader('Content-Type', 'text/plain;charset=utf8');
fs.createReadStream(p, { start, end }).pipe(res);
}); serevr.listen(3000);
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。