range 表示,当客户端向服务端发送请求时,可以申明请求内容的范围,比如从多少字节到多少字节。
要实现Range,只需要:
- 在请求的时候,在Request Headers 里面放一个range字段,来申明想要的范围,如 range: bytes=[start]-[end]
- 在响应中,我们也需要加一个响应头,表示服务器可以处理的数据格式为字节,Accept-Ranges: bytes
- 在Response Headers 里,返回一个 Content-Range: bytes start-end/total
下面我们来写服务器端处理range 的代码。
先在 src/helper 下创建文件 range.js 文件,在这里,处理 range 的功能。代码如下。
module.exports = (totalSize, req, res) => {
const range = req.headers['range'];
if (!range) {
return {code: 200}
}
const sizes = range.match(/range=(\d*)-(\d*)/);
const end = sizes[2] || totalSize - 1;
const start = sizes[1] || totalSize - end;
if (start > end || start < 0 || end > totalSize) {
return {code: 200}
}
// 返回部分内容
res.setHeader('Accept-Range', 'bytes');
res.setHeader('Content-Range',`bytes ${start}-${end}/${totalSize}`);
res.setHeader('Content-Length', end - start);
return {
code: 206,
start: parseInt(start),
end: parseInt(end)
}
}
然后,我们在 route.js 中引用,如下。
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
const promisify = require('util').promisify;
const conf = require('../config/defaultConfig');
const mime = require('./mime');
const compress = require('./compress');
const range = require('./range');
const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);
const ejsPath = path.join(__dirname, '../templates/main-page.ejs');
const source = fs.readFileSync(ejsPath,'utf-8');
module.exports = async function (req, res, filePath) {
try {
const stats = await stat(filePath);
if (stats.isFile()) {
const contentType = mime(filePath);
res.setHeader('Content-Type', contentType);
let rs;
const {code, start, end} = range(stats.size, req, res);
if (code === 200) {
res.statusCode = 200;
rs = fs.createReadStream(filePath);
} else {
res.statusCode = code;
rs = fs.createReadStream(filePath,{start: start, end: end});
}
if (filePath.match(conf.compress)) {
rs = compress(rs, req, res)
}
rs.pipe(res)
}
if (stats.isDirectory()) {
const files = await readdir(filePath);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
const dir = path.relative(conf.root, filePath)
const data = {
title: path.basename(filePath),
dir: dir ? `/${dir}` : '',
files: files.map((file) => {
return {
file,
icon: mime(file)
}
})
};
res.end(ejs.render(source, data));
}
} catch(ex) {
console.log(ex);
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end(`${filePath} is not a file or directory`);
}
}
Done.