Node.js 写一个简单的静态资源服务器 7 range范围请求

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.

猜你喜欢

转载自blog.csdn.net/purple_lumpy/article/details/89556172
今日推荐